Skip to content

Commit 0d44641

Browse files
byrootetiennebarrie
andcommitted
Refactor generic fields to use T_IMEMO/fields objects.
Followup: ruby#13589 This simplify a lot of things, as we no longer need to manually manage the memory, we can use the Read-Copy-Update pattern and avoid numerous race conditions. Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
1 parent cec84ff commit 0d44641

9 files changed

Lines changed: 347 additions & 383 deletions

File tree

gc.c

Lines changed: 32 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,41 +2015,29 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
20152015
static inline void
20162016
obj_free_object_id(VALUE obj)
20172017
{
2018-
if (RB_BUILTIN_TYPE(obj) == T_IMEMO) {
2019-
return;
2020-
}
2021-
2022-
#if RUBY_DEBUG
2023-
switch (BUILTIN_TYPE(obj)) {
2024-
case T_CLASS:
2025-
case T_MODULE:
2026-
break;
2027-
default:
2028-
if (rb_shape_obj_has_id(obj)) {
2029-
VALUE id = object_id_get(obj, RBASIC_SHAPE_ID(obj)); // Crash if missing
2030-
if (!(FIXNUM_P(id) || RB_TYPE_P(id, T_BIGNUM))) {
2031-
rb_p(obj);
2032-
rb_bug("Corrupted object_id");
2033-
}
2034-
}
2035-
break;
2036-
}
2037-
#endif
2038-
20392018
VALUE obj_id = 0;
20402019
if (RB_UNLIKELY(id2ref_tbl)) {
20412020
switch (BUILTIN_TYPE(obj)) {
20422021
case T_CLASS:
20432022
case T_MODULE:
20442023
obj_id = RCLASS(obj)->object_id;
20452024
break;
2046-
default: {
2025+
case T_IMEMO:
2026+
if (!IMEMO_TYPE_P(obj, imemo_fields)) {
2027+
return;
2028+
}
2029+
// fallthrough
2030+
case T_OBJECT:
2031+
{
20472032
shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
20482033
if (rb_shape_has_object_id(shape_id)) {
20492034
obj_id = object_id_get(obj, shape_id);
20502035
}
20512036
break;
20522037
}
2038+
default:
2039+
// For generic_fields, the T_IMEMO/fields is responsible for freeing the id.
2040+
return;
20532041
}
20542042

20552043
if (RB_UNLIKELY(obj_id)) {
@@ -2316,10 +2304,6 @@ rb_obj_memsize_of(VALUE obj)
23162304
return 0;
23172305
}
23182306

2319-
if (rb_obj_exivar_p(obj)) {
2320-
size += rb_generic_ivar_memsize(obj);
2321-
}
2322-
23232307
switch (BUILTIN_TYPE(obj)) {
23242308
case T_OBJECT:
23252309
if (rb_shape_obj_too_complex_p(obj)) {
@@ -3936,38 +3920,6 @@ vm_weak_table_foreach_update_weak_value(st_data_t *key, st_data_t *value, st_dat
39363920
return iter_data->update_callback((VALUE *)value, iter_data->data);
39373921
}
39383922

3939-
static void
3940-
free_gen_fields_tbl(VALUE obj, struct gen_fields_tbl *fields_tbl)
3941-
{
3942-
if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
3943-
st_free_table(fields_tbl->as.complex.table);
3944-
}
3945-
3946-
xfree(fields_tbl);
3947-
}
3948-
3949-
static int
3950-
vm_weak_table_gen_fields_foreach_too_complex_i(st_data_t _key, st_data_t value, st_data_t data, int error)
3951-
{
3952-
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
3953-
3954-
GC_ASSERT(!iter_data->weak_only);
3955-
3956-
if (SPECIAL_CONST_P((VALUE)value)) return ST_CONTINUE;
3957-
3958-
return iter_data->callback((VALUE)value, iter_data->data);
3959-
}
3960-
3961-
static int
3962-
vm_weak_table_gen_fields_foreach_too_complex_replace_i(st_data_t *_key, st_data_t *value, st_data_t data, int existing)
3963-
{
3964-
struct global_vm_table_foreach_data *iter_data = (struct global_vm_table_foreach_data *)data;
3965-
3966-
GC_ASSERT(!iter_data->weak_only);
3967-
3968-
return iter_data->update_callback((VALUE *)value, iter_data->data);
3969-
}
3970-
39713923
struct st_table *rb_generic_fields_tbl_get(void);
39723924

39733925
static int
@@ -4004,60 +3956,50 @@ vm_weak_table_gen_fields_foreach(st_data_t key, st_data_t value, st_data_t data)
40043956

40053957
int ret = iter_data->callback((VALUE)key, iter_data->data);
40063958

3959+
VALUE new_value = (VALUE)value;
3960+
VALUE new_key = (VALUE)key;
3961+
40073962
switch (ret) {
40083963
case ST_CONTINUE:
40093964
break;
40103965

40113966
case ST_DELETE:
4012-
free_gen_fields_tbl((VALUE)key, (struct gen_fields_tbl *)value);
40133967
RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID);
40143968
return ST_DELETE;
40153969

40163970
case ST_REPLACE: {
4017-
VALUE new_key = (VALUE)key;
40183971
ret = iter_data->update_callback(&new_key, iter_data->data);
4019-
if (key != new_key) ret = ST_DELETE;
4020-
DURING_GC_COULD_MALLOC_REGION_START();
4021-
{
4022-
st_insert(rb_generic_fields_tbl_get(), (st_data_t)new_key, value);
3972+
if (key != new_key) {
3973+
ret = ST_DELETE;
40233974
}
4024-
DURING_GC_COULD_MALLOC_REGION_END();
4025-
key = (st_data_t)new_key;
40263975
break;
40273976
}
40283977

40293978
default:
4030-
return ret;
3979+
rb_bug("vm_weak_table_gen_fields_foreach: return value %d not supported", ret);
40313980
}
40323981

40333982
if (!iter_data->weak_only) {
4034-
struct gen_fields_tbl *fields_tbl = (struct gen_fields_tbl *)value;
3983+
int ivar_ret = iter_data->callback(new_value, iter_data->data);
3984+
switch (ivar_ret) {
3985+
case ST_CONTINUE:
3986+
break;
40353987

4036-
if (rb_shape_obj_too_complex_p((VALUE)key)) {
4037-
st_foreach_with_replace(
4038-
fields_tbl->as.complex.table,
4039-
vm_weak_table_gen_fields_foreach_too_complex_i,
4040-
vm_weak_table_gen_fields_foreach_too_complex_replace_i,
4041-
data
4042-
);
3988+
case ST_REPLACE:
3989+
iter_data->update_callback(&new_value, iter_data->data);
3990+
break;
3991+
3992+
default:
3993+
rb_bug("vm_weak_table_gen_fields_foreach: return value %d not supported", ivar_ret);
40433994
}
4044-
else {
4045-
uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID((VALUE)key));
4046-
for (uint32_t i = 0; i < fields_count; i++) {
4047-
if (SPECIAL_CONST_P(fields_tbl->as.shape.fields[i])) continue;
3995+
}
40483996

4049-
int ivar_ret = iter_data->callback(fields_tbl->as.shape.fields[i], iter_data->data);
4050-
switch (ivar_ret) {
4051-
case ST_CONTINUE:
4052-
break;
4053-
case ST_REPLACE:
4054-
iter_data->update_callback(&fields_tbl->as.shape.fields[i], iter_data->data);
4055-
break;
4056-
default:
4057-
rb_bug("vm_weak_table_gen_fields_foreach: return value %d not supported", ivar_ret);
4058-
}
4059-
}
3997+
if (key != new_key || value != new_value) {
3998+
DURING_GC_COULD_MALLOC_REGION_START();
3999+
{
4000+
st_insert(rb_generic_fields_tbl_get(), (st_data_t)new_key, new_value);
40604001
}
4002+
DURING_GC_COULD_MALLOC_REGION_END();
40614003
}
40624004

40634005
return ret;

imemo.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,23 @@ rb_imemo_fields_new_complex(VALUE klass, size_t capa)
147147
return imemo_fields_new_complex(klass, capa);
148148
}
149149

150+
static int
151+
imemo_fields_trigger_wb_i(st_data_t key, st_data_t value, st_data_t arg)
152+
{
153+
VALUE field_obj = (VALUE)arg;
154+
RB_OBJ_WRITTEN(field_obj, Qundef, (VALUE)value);
155+
return ST_CONTINUE;
156+
}
157+
158+
VALUE
159+
rb_imemo_fields_new_complex_tbl(VALUE klass, st_table *tbl)
160+
{
161+
VALUE fields = imemo_fields_new(klass, sizeof(struct rb_fields));
162+
IMEMO_OBJ_FIELDS(fields)->as.complex.table = tbl;
163+
st_foreach(tbl, imemo_fields_trigger_wb_i, (st_data_t)fields);
164+
return fields;
165+
}
166+
150167
VALUE
151168
rb_imemo_fields_clone(VALUE fields_obj)
152169
{

internal/imemo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ struct rb_fields {
280280

281281
VALUE rb_imemo_fields_new(VALUE klass, size_t capa);
282282
VALUE rb_imemo_fields_new_complex(VALUE klass, size_t capa);
283+
VALUE rb_imemo_fields_new_complex_tbl(VALUE klass, st_table *tbl);
283284
VALUE rb_imemo_fields_clone(VALUE fields_obj);
284285

285286
static inline VALUE *

internal/variable.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
/* variable.c */
1919
void rb_gc_mark_global_tbl(void);
2020
void rb_gc_update_global_tbl(void);
21-
size_t rb_generic_ivar_memsize(VALUE);
2221
VALUE rb_search_class_path(VALUE);
2322
VALUE rb_attr_delete(VALUE, ID);
2423
void rb_autoload_str(VALUE mod, ID id, VALUE file);
@@ -47,8 +46,7 @@ void rb_gvar_namespace_ready(const char *name);
4746
*/
4847
VALUE rb_mod_set_temporary_name(VALUE, VALUE);
4948

50-
struct gen_fields_tbl;
51-
int rb_gen_fields_tbl_get(VALUE obj, ID id, struct gen_fields_tbl **fields_tbl);
49+
int rb_gen_fields_tbl_get(VALUE obj, ID id, VALUE *fields_obj);
5250
void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table);
5351
void rb_obj_init_too_complex(VALUE obj, st_table *table);
5452
void rb_evict_ivars_to_hash(VALUE obj);

ractor.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,8 +1657,8 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
16571657
} while (0)
16581658

16591659
if (UNLIKELY(rb_obj_exivar_p(obj))) {
1660-
struct gen_fields_tbl *fields_tbl;
1661-
rb_ivar_generic_fields_tbl_lookup(obj, &fields_tbl);
1660+
VALUE fields_obj;
1661+
rb_ivar_generic_fields_tbl_lookup(obj, &fields_obj);
16621662

16631663
if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
16641664
struct obj_traverse_replace_callback_data d = {
@@ -1667,7 +1667,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
16671667
.src = obj,
16681668
};
16691669
rb_st_foreach_with_replace(
1670-
fields_tbl->as.complex.table,
1670+
rb_imemo_fields_complex_tbl(fields_obj),
16711671
obj_iv_hash_traverse_replace_foreach_i,
16721672
obj_iv_hash_traverse_replace_i,
16731673
(st_data_t)&d
@@ -1676,8 +1676,9 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
16761676
}
16771677
else {
16781678
uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
1679+
VALUE *fields = rb_imemo_fields_ptr(fields_obj);
16791680
for (uint32_t i = 0; i < fields_count; i++) {
1680-
CHECK_AND_REPLACE(fields_tbl->as.shape.fields[i]);
1681+
CHECK_AND_REPLACE(fields[i]);
16811682
}
16821683
}
16831684
}

test/ruby/test_encoding.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_singleton
3333
encodings.each do |e|
3434
assert_raise(TypeError) { e.dup }
3535
assert_raise(TypeError) { e.clone }
36-
assert_equal(e.object_id, Marshal.load(Marshal.dump(e)).object_id)
36+
assert_same(e, Marshal.load(Marshal.dump(e)))
3737
end
3838
end
3939

0 commit comments

Comments
 (0)