refactoring rb_obj_traverse()

* create rec check hash lazily
* do not pass *data pointer for enter/leave function because
  it is not used.
This commit is contained in:
Koichi Sasada 2020-10-21 23:00:36 +09:00
parent af2471365f
commit 89f6644de7
Notes: git 2020-10-21 23:52:36 +09:00

View File

@ -1870,15 +1870,15 @@ rb_ractor_stderr_set(VALUE err)
// 2: stop search // 2: stop search
// 1: skip child // 1: skip child
// 0: continue // 0: continue
typedef int (*rb_obj_traverse_enter_func)(VALUE obj, void *data); typedef int (*rb_obj_traverse_enter_func)(VALUE obj);
typedef int (*rb_obj_traverse_leave_func)(VALUE obj, void *data); typedef int (*rb_obj_traverse_leave_func)(VALUE obj);
struct obj_traverse_data { struct obj_traverse_data {
rb_obj_traverse_enter_func enter_func; rb_obj_traverse_enter_func enter_func;
rb_obj_traverse_leave_func leave_func; rb_obj_traverse_leave_func leave_func;
void *data;
st_table *rec; st_table *rec;
VALUE rec_hash;
}; };
@ -1887,19 +1887,19 @@ struct obj_traverse_callback_data {
struct obj_traverse_data *data; struct obj_traverse_data *data;
}; };
static int rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data); static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
static int static int
obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr) obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
{ {
struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
if (rb_obj_traverse_i(key, d->data)) { if (obj_traverse_i(key, d->data)) {
d->stop = true; d->stop = true;
return ST_STOP; return ST_STOP;
} }
if (rb_obj_traverse_i(val, d->data)) { if (obj_traverse_i(val, d->data)) {
d->stop = true; d->stop = true;
return ST_STOP; return ST_STOP;
} }
@ -1912,24 +1912,34 @@ obj_tdata_traverse_i(VALUE obj, void *ptr)
{ {
struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
if (rb_obj_traverse_i(obj, d->data)) { if (obj_traverse_i(obj, d->data)) {
d->stop = true; d->stop = true;
} }
} }
static struct st_table *
obj_traverse_rec(struct obj_traverse_data *data)
{
if (UNLIKELY(!data->rec)) {
data->rec_hash = rb_ident_hash_new();
data->rec = rb_hash_st_table(data->rec_hash);
}
return data->rec;
}
static int static int
rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data) obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
{ {
if (RB_SPECIAL_CONST_P(obj)) return 0; if (RB_SPECIAL_CONST_P(obj)) return 0;
switch (data->enter_func(obj, data->data)) { switch (data->enter_func(obj)) {
case 0: break; case 0: break;
case 1: return 0; // skip children case 1: return 0; // skip children
case 2: return 1; // stop search case 2: return 1; // stop search
default: rb_bug("rb_obj_traverse_func should return 0 to 2"); default: rb_bug("rb_obj_traverse_func should return 0 to 2");
} }
if (st_insert(data->rec, obj, 1)) { if (st_insert(obj_traverse_rec(data), obj, 1)) {
// already traversed // already traversed
return 0; return 0;
} }
@ -1939,7 +1949,7 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) { for (uint32_t i = 0; i < ivtbl->numiv; i++) {
VALUE val = ivtbl->ivptr[i]; VALUE val = ivtbl->ivptr[i];
if (val != Qundef && rb_obj_traverse_i(val, data)) return 1; if (val != Qundef && obj_traverse_i(val, data)) return 1;
} }
} }
@ -1961,7 +1971,7 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
for (uint32_t i=0; i<len; i++) { for (uint32_t i=0; i<len; i++) {
VALUE val = ptr[i]; VALUE val = ptr[i];
if (val != Qundef && rb_obj_traverse_i(val, data)) return 1; if (val != Qundef && obj_traverse_i(val, data)) return 1;
} }
} }
break; break;
@ -1970,14 +1980,14 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
{ {
for (int i = 0; i < RARRAY_LENINT(obj); i++) { for (int i = 0; i < RARRAY_LENINT(obj); i++) {
VALUE e = rb_ary_entry(obj, i); VALUE e = rb_ary_entry(obj, i);
if (rb_obj_traverse_i(e, data)) return 1; if (obj_traverse_i(e, data)) return 1;
} }
} }
break; break;
case T_HASH: case T_HASH:
{ {
if (rb_obj_traverse_i(RHASH_IFNONE(obj), data)) return 1; if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
struct obj_traverse_callback_data d = { struct obj_traverse_callback_data d = {
.stop = false, .stop = false,
@ -1994,18 +2004,18 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
const VALUE *ptr = RSTRUCT_CONST_PTR(obj); const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
for (long i=0; i<len; i++) { for (long i=0; i<len; i++) {
if (rb_obj_traverse_i(ptr[i], data)) return 1; if (obj_traverse_i(ptr[i], data)) return 1;
} }
} }
break; break;
case T_RATIONAL: case T_RATIONAL:
if (rb_obj_traverse_i(RRATIONAL(obj)->num, data)) return 1; if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
if (rb_obj_traverse_i(RRATIONAL(obj)->den, data)) return 1; if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
break; break;
case T_COMPLEX: case T_COMPLEX:
if (rb_obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1; if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
if (rb_obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1; if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
break; break;
case T_DATA: case T_DATA:
@ -2028,7 +2038,7 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
rb_bug("unreachable"); rb_bug("unreachable");
} }
switch (data->leave_func(obj, data->data)) { switch (data->leave_func(obj)) {
case 0: case 0:
case 1: return 0; // terminate case 1: return 0; // terminate
case 2: return 1; // stop search case 2: return 1; // stop search
@ -2041,21 +2051,15 @@ rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
static int static int
rb_obj_traverse(VALUE obj, rb_obj_traverse(VALUE obj,
rb_obj_traverse_enter_func enter_func, rb_obj_traverse_enter_func enter_func,
rb_obj_traverse_leave_func leave_func, rb_obj_traverse_leave_func leave_func)
void *passed_data)
{ {
VALUE h = rb_ident_hash_new();
struct obj_traverse_data data = { struct obj_traverse_data data = {
.enter_func = enter_func, .enter_func = enter_func,
.leave_func = leave_func, .leave_func = leave_func,
.data = passed_data, .rec = NULL,
.rec = rb_hash_st_table(h),
}; };
int r = rb_obj_traverse_i(obj, &data); return obj_traverse_i(obj, &data);
RB_GC_GUARD(h);
return r;
} }
static int static int
@ -2076,7 +2080,7 @@ frozen_shareable_p(VALUE obj)
} }
static int static int
make_shareable_check_shareable(VALUE obj, void *data) make_shareable_check_shareable(VALUE obj)
{ {
VM_ASSERT(!SPECIAL_CONST_P(obj)); VM_ASSERT(!SPECIAL_CONST_P(obj));
@ -2091,12 +2095,16 @@ make_shareable_check_shareable(VALUE obj, void *data)
if (!OBJ_FROZEN(obj)) { if (!OBJ_FROZEN(obj)) {
rb_funcall(obj, idFreeze, 0); rb_funcall(obj, idFreeze, 0);
if (UNLIKELY(!OBJ_FROZEN(obj))) {
rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
}
} }
return 0; return 0;
} }
static int static int
mark_shareable(VALUE obj, void *data) mark_shareable(VALUE obj)
{ {
FL_SET_RAW(obj, RUBY_FL_SHAREABLE); FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
return 0; return 0;
@ -2107,13 +2115,12 @@ rb_ractor_make_shareable(VALUE obj)
{ {
rb_obj_traverse(obj, rb_obj_traverse(obj,
make_shareable_check_shareable, make_shareable_check_shareable,
mark_shareable, mark_shareable);
NULL);
return obj; return obj;
} }
static int static int
shareable_p_enter(VALUE obj, void *ptr) shareable_p_enter(VALUE obj)
{ {
if (RB_OBJ_SHAREABLE_P(obj)) { if (RB_OBJ_SHAREABLE_P(obj)) {
return 1; return 1;
@ -2122,7 +2129,7 @@ shareable_p_enter(VALUE obj, void *ptr)
RB_TYPE_P(obj, T_MODULE) || RB_TYPE_P(obj, T_MODULE) ||
RB_TYPE_P(obj, T_ICLASS)) { RB_TYPE_P(obj, T_ICLASS)) {
// TODO: remove it // TODO: remove it
mark_shareable(obj, NULL); mark_shareable(obj);
return 1; return 1;
} }
else if (RB_OBJ_FROZEN_RAW(obj) && else if (RB_OBJ_FROZEN_RAW(obj) &&
@ -2138,8 +2145,7 @@ rb_ractor_shareable_p_continue(VALUE obj)
{ {
if (rb_obj_traverse(obj, if (rb_obj_traverse(obj,
shareable_p_enter, shareable_p_enter,
mark_shareable, mark_shareable)) {
NULL)) {
return false; return false;
} }
else { else {