Optionally traverse non-weak references in rb_gc_vm_weak_table_foreach

For moving garbage collectors, we may want to combine liveliness checking
with reference updating for performance. This commit allows for non-weak
references to be passed into the callback function when weak_only is false.
This commit is contained in:
Peter Zhu 2025-01-23 10:28:29 -05:00
parent c3c7300b89
commit 9e5ff79c5b
Notes: git 2025-01-27 15:28:55 +00:00
2 changed files with 124 additions and 24 deletions

146
gc.c
View File

@ -3346,22 +3346,39 @@ struct global_vm_table_foreach_data {
vm_table_foreach_callback_func callback;
vm_table_update_callback_func update_callback;
void *data;
bool weak_only;
};
static int
vm_weak_table_foreach_key(st_data_t key, st_data_t value, st_data_t data, int error)
vm_weak_table_foreach_weak_key(st_data_t key, st_data_t value, st_data_t data, int error)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
return iter_data->callback((VALUE)key, iter_data->data);
int ret = iter_data->callback((VALUE)key, iter_data->data);
if (!iter_data->weak_only) {
if (ret != ST_CONTINUE) return ret;
ret = iter_data->callback((VALUE)value, iter_data->data);
}
return ret;
}
static int
vm_weak_table_foreach_update_key(st_data_t *key, st_data_t *value, st_data_t data, int existing)
vm_weak_table_foreach_update_weak_key(st_data_t *key, st_data_t *value, st_data_t data, int existing)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
return iter_data->update_callback((VALUE *)key, iter_data->data);
int ret = iter_data->update_callback((VALUE *)key, iter_data->data);
if (!iter_data->weak_only) {
if (ret != ST_CONTINUE) return ret;
ret = iter_data->update_callback((VALUE *)value, iter_data->data);
}
return ret;
}
static int
@ -3369,6 +3386,11 @@ vm_weak_table_str_sym_foreach(st_data_t key, st_data_t value, st_data_t data, in
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
if (!iter_data->weak_only) {
int ret = iter_data->callback((VALUE)key, iter_data->data);
if (ret != ST_CONTINUE) return ret;
}
if (STATIC_SYM_P(value)) {
return ST_CONTINUE;
}
@ -3378,10 +3400,15 @@ vm_weak_table_str_sym_foreach(st_data_t key, st_data_t value, st_data_t data, in
}
static int
vm_weak_table_foreach_update_value(st_data_t *key, st_data_t *value, st_data_t data, int existing)
vm_weak_table_foreach_update_weak_value(st_data_t *key, st_data_t *value, st_data_t data, int existing)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
if (!iter_data->weak_only) {
int ret = iter_data->update_callback((VALUE *)key, iter_data->data);
if (ret != ST_CONTINUE) return ret;
}
return iter_data->update_callback((VALUE *)value, iter_data->data);
}
@ -3396,35 +3423,108 @@ free_gen_ivtbl(VALUE obj, struct gen_ivtbl *ivtbl)
}
static int
vm_weak_table_gen_ivar_foreach(st_data_t key, st_data_t value, st_data_t data, int error)
vm_weak_table_gen_ivar_foreach_too_complex_i(st_data_t _key, st_data_t value, st_data_t data, int error)
{
int retval = vm_weak_table_foreach_key(key, value, data, error);
if (retval == ST_DELETE) {
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
GC_ASSERT(!iter_data->weak_only);
return iter_data->callback((VALUE)value, iter_data->data);
}
static int
vm_weak_table_gen_ivar_foreach_too_complex_replace_i(st_data_t *_key, st_data_t *value, st_data_t data, int existing)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
GC_ASSERT(!iter_data->weak_only);
return iter_data->update_callback((VALUE *)value, iter_data->data);
}
struct st_table *rb_generic_ivtbl_get(void);
static int
vm_weak_table_gen_ivar_foreach(st_data_t key, st_data_t value, st_data_t data)
{
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
int ret = iter_data->callback((VALUE)key, iter_data->data);
switch (ret) {
case ST_CONTINUE:
break;
case ST_DELETE:
free_gen_ivtbl((VALUE)key, (struct gen_ivtbl *)value);
FL_UNSET((VALUE)key, FL_EXIVAR);
return ST_DELETE;
case ST_REPLACE: {
VALUE new_key = (VALUE)key;
ret = iter_data->update_callback(&new_key, iter_data->data);
if (key != new_key) ret = ST_DELETE;
DURING_GC_COULD_MALLOC_REGION_START();
{
st_insert(rb_generic_ivtbl_get(), (st_data_t)new_key, value);
}
DURING_GC_COULD_MALLOC_REGION_END();
key = (st_data_t)new_key;
break;
}
default:
return ret;
}
return retval;
if (!iter_data->weak_only) {
struct gen_ivtbl *ivtbl = (struct gen_ivtbl *)value;
if (rb_shape_obj_too_complex((VALUE)key)) {
st_foreach_with_replace(
ivtbl->as.complex.table,
vm_weak_table_gen_ivar_foreach_too_complex_i,
vm_weak_table_gen_ivar_foreach_too_complex_replace_i,
data
);
}
else {
for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
if (SPECIAL_CONST_P(ivtbl->as.shape.ivptr[i])) continue;
int ivar_ret = iter_data->callback(ivtbl->as.shape.ivptr[i], iter_data->data);
switch (ivar_ret) {
case ST_CONTINUE:
break;
case ST_REPLACE:
iter_data->update_callback(&ivtbl->as.shape.ivptr[i], iter_data->data);
break;
default:
rb_bug("vm_weak_table_gen_ivar_foreach: return value %d not supported", ivar_ret);
}
}
}
}
return ret;
}
static int
vm_weak_table_frozen_strings_foreach(st_data_t key, st_data_t value, st_data_t data, int error)
{
GC_ASSERT(RB_TYPE_P((VALUE)key, T_STRING));
int retval = vm_weak_table_foreach_key(key, value, data, error);
int retval = vm_weak_table_foreach_weak_key(key, value, data, error);
if (retval == ST_DELETE) {
FL_UNSET((VALUE)key, RSTRING_FSTR);
}
return retval;
}
struct st_table *rb_generic_ivtbl_get(void);
void
rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
vm_table_update_callback_func update_callback,
void *data,
bool weak_only,
enum rb_gc_vm_weak_tables table)
{
rb_vm_t *vm = GET_VM();
@ -3432,7 +3532,8 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
struct global_vm_table_foreach_data foreach_data = {
.callback = callback,
.update_callback = update_callback,
.data = data
.data = data,
.weak_only = weak_only,
};
switch (table) {
@ -3440,8 +3541,8 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
if (vm->ci_table) {
st_foreach_with_replace(
vm->ci_table,
vm_weak_table_foreach_key,
vm_weak_table_foreach_update_key,
vm_weak_table_foreach_weak_key,
vm_weak_table_foreach_update_weak_key,
(st_data_t)&foreach_data
);
}
@ -3451,8 +3552,8 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
if (vm->overloaded_cme_table) {
st_foreach_with_replace(
vm->overloaded_cme_table,
vm_weak_table_foreach_key,
vm_weak_table_foreach_update_key,
vm_weak_table_foreach_weak_key,
vm_weak_table_foreach_update_weak_key,
(st_data_t)&foreach_data
);
}
@ -3463,7 +3564,7 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
st_foreach_with_replace(
global_symbols.str_sym,
vm_weak_table_str_sym_foreach,
vm_weak_table_foreach_update_value,
vm_weak_table_foreach_update_weak_value,
(st_data_t)&foreach_data
);
}
@ -3472,10 +3573,9 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
case RB_GC_VM_GENERIC_IV_TABLE: {
st_table *generic_iv_tbl = rb_generic_ivtbl_get();
if (generic_iv_tbl) {
st_foreach_with_replace(
st_foreach(
generic_iv_tbl,
vm_weak_table_gen_ivar_foreach,
vm_weak_table_foreach_update_key,
(st_data_t)&foreach_data
);
}
@ -3486,7 +3586,7 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
st_foreach_with_replace(
vm->frozen_strings,
vm_weak_table_frozen_strings_foreach,
vm_weak_table_foreach_update_key,
vm_weak_table_foreach_update_weak_key,
(st_data_t)&foreach_data
);
}

View File

@ -77,7 +77,7 @@ void *rb_gc_get_ractor_newobj_cache(void);
void rb_gc_initialize_vm_context(struct rb_gc_vm_context *context);
void rb_gc_worker_thread_set_vm_context(struct rb_gc_vm_context *context);
void rb_gc_worker_thread_unset_vm_context(struct rb_gc_vm_context *context);
void rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback, vm_table_update_callback_func update_callback, void *data, enum rb_gc_vm_weak_tables table);
void rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback, vm_table_update_callback_func update_callback, void *data, bool weak_only, enum rb_gc_vm_weak_tables table);
#endif
RUBY_SYMBOL_EXPORT_END