From e03d6d9eb84764cb854dc500ce8c19d85db53656 Mon Sep 17 00:00:00 2001 From: ko1 Date: Fri, 5 Oct 2012 08:14:09 +0000 Subject: * ext/objspace/objspace.c: add ObjectSpace#reachable_objects_from. This method returns an array of objects referenced by given object. If given object is special objects such as true/false/nil/Fixnum etc then it returns nil. See rdoc for details. [ruby-core:39772] * test/objspace/test_objspace.rb: add a test for this method. * gc.c: add rb_objspace_reachable_objects_from(). To make this function, add several member `mark_func_data' to rb_objspace_t. If mark_func_data is not null, then gc_mark() calls mark_func_data::mark_func. * gc.h: export rb_objspace_reachable_objects_from(). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37094 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- gc.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 29 deletions(-) (limited to 'gc.c') diff --git a/gc.c b/gc.c index e87e029596..d34e70940b 100644 --- a/gc.c +++ b/gc.c @@ -273,6 +273,11 @@ typedef struct rb_objspace { struct gc_list *global_list; size_t count; int gc_stress; + + struct mark_func_data_struct { + VALUE data; + void (*mark_func)(struct rb_objspace *objspace, VALUE v); + } *mark_func_data; } rb_objspace_t; #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE @@ -1137,31 +1142,41 @@ struct os_each_struct { VALUE of; }; +static int +internal_object_p(VALUE obj) +{ + RVALUE *p = (RVALUE *)obj; + + if (p->as.basic.flags) { + switch (BUILTIN_TYPE(p)) { + case T_NONE: + case T_ICLASS: + case T_NODE: + case T_ZOMBIE: + break; + case T_CLASS: + if (FL_TEST(p, FL_SINGLETON)) + break; + default: + if (!p->as.basic.klass) break; + return 0; + } + } + return 1; +} + static int os_obj_of_i(void *vstart, void *vend, size_t stride, void *data) { struct os_each_struct *oes = (struct os_each_struct *)data; RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend; - volatile VALUE v; for (; p != pend; p++) { - if (p->as.basic.flags) { - switch (BUILTIN_TYPE(p)) { - case T_NONE: - case T_ICLASS: - case T_NODE: - case T_ZOMBIE: - continue; - case T_CLASS: - if (FL_TEST(p, FL_SINGLETON)) - continue; - default: - if (!p->as.basic.klass) continue; - v = (VALUE)p; - if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { - rb_yield(v); - oes->num++; - } + volatile VALUE v = (VALUE)p; + if (!internal_object_p(v)) { + if (!oes->of || rb_obj_is_kind_of(v, oes->of)) { + rb_yield(v); + oes->num++; } } } @@ -2521,17 +2536,31 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr) return 1; } +static int +markable_object_p(rb_objspace_t *objspace, VALUE ptr) +{ + register RVALUE *obj = RANY(ptr); + + if (rb_special_const_p(ptr)) return 0; /* special const not marked */ + if (obj->as.basic.flags == 0) return 0 ; /* free cell */ + + return 1; +} + static void gc_mark(rb_objspace_t *objspace, VALUE ptr) { - register RVALUE *obj; - - obj = RANY(ptr); - if (rb_special_const_p(ptr)) return; /* special const not marked */ - if (obj->as.basic.flags == 0) return; /* free cell */ - if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + if (!markable_object_p(objspace, ptr)) { + return; + } - push_mark_stack(&objspace->mark_stack, ptr); + if (LIKELY(objspace->mark_func_data == 0)) { + if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + push_mark_stack(&objspace->mark_stack, ptr); + } + else { + objspace->mark_func_data->mark_func(objspace, ptr); + } } void @@ -2548,10 +2577,16 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr) goto marking; /* skip */ again: - obj = RANY(ptr); - if (rb_special_const_p(ptr)) return; /* special const not marked */ - if (obj->as.basic.flags == 0) return; /* free cell */ - if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + if (LIKELY(objspace->mark_func_data == 0)) { + obj = RANY(ptr); + if (rb_special_const_p(ptr)) return; /* special const not marked */ + if (obj->as.basic.flags == 0) return; /* free cell */ + if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */ + } + else { + gc_mark(objspace, ptr); + return; + } marking: if (FL_TEST(obj, FL_EXIVAR)) { @@ -2953,6 +2988,8 @@ rb_gc_unregister_address(VALUE *addr) static int garbage_collect(rb_objspace_t *objspace) { + struct mark_func_data_struct *prev_mark_func_data; + if (GC_NOTIFY) printf("start garbage_collect()\n"); if (!heaps) { @@ -2964,6 +3001,9 @@ garbage_collect(rb_objspace_t *objspace) gc_prof_timer_start(objspace); + prev_mark_func_data = objspace->mark_func_data; + objspace->mark_func_data = 0; + rest_sweep(objspace); during_gc++; @@ -2973,6 +3013,8 @@ garbage_collect(rb_objspace_t *objspace) gc_sweep(objspace); gc_prof_sweep_timer_stop(objspace); + objspace->mark_func_data = prev_mark_func_data; + gc_prof_timer_stop(objspace, Qtrue); if (GC_NOTIFY) printf("end garbage_collect()\n"); return TRUE; @@ -3241,6 +3283,46 @@ rb_gc_set_params(void) } } +static void +collect_refs(rb_objspace_t *objspace, VALUE obj) +{ + if (markable_object_p(objspace, obj) && !internal_object_p(obj)) { + st_insert((st_table *)objspace->mark_func_data->data, obj, Qtrue); + } +} + +static int +collect_keys(st_data_t key, st_data_t value, st_data_t data) +{ + VALUE ary = (VALUE)data; + rb_ary_push(ary, (VALUE)key); + return ST_CONTINUE; +} + +VALUE +rb_objspace_reachable_objects_from(VALUE obj) +{ + rb_objspace_t *objspace = &rb_objspace; + + if (markable_object_p(objspace, obj)) { + st_table *refs = st_init_numtable(); + struct mark_func_data_struct mfd; + VALUE ret = rb_ary_new(); + mfd.mark_func = collect_refs; + mfd.data = (VALUE)refs; + objspace->mark_func_data = &mfd; + + gc_mark_children(objspace, obj); + + objspace->mark_func_data = 0; + st_foreach(refs, collect_keys, (st_data_t)ret); + return ret; + } + else { + return Qnil; + } +} + /* ------------------------ Extended allocator ------------------------ */ -- cgit v1.2.3