Skip to content

Commit a2c633a

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 7885e67 commit a2c633a

10 files changed

Lines changed: 352 additions & 401 deletions

File tree

ext/objspace/objspace_dump.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,10 @@ dump_object(VALUE obj, struct dump_config *dc)
394394

395395
dc->cur_obj = obj;
396396
dc->cur_obj_references = 0;
397-
if (BUILTIN_TYPE(obj) == T_NODE || BUILTIN_TYPE(obj) == T_IMEMO) {
397+
if (BUILTIN_TYPE(obj) == T_NODE || (BUILTIN_TYPE(obj) == T_IMEMO && !IMEMO_TYPE_P(obj, imemo_fields))) {
398398
dc->cur_obj_klass = 0;
399-
} else {
399+
}
400+
else {
400401
dc->cur_obj_klass = RBASIC_CLASS(obj);
401402
}
402403

@@ -414,8 +415,8 @@ dump_object(VALUE obj, struct dump_config *dc)
414415
dump_append(dc, obj_type(obj));
415416
dump_append(dc, "\"");
416417

417-
if (BUILTIN_TYPE(obj) != T_IMEMO) {
418-
size_t shape_id = rb_obj_shape_id(obj);
418+
if (BUILTIN_TYPE(obj) != T_IMEMO || IMEMO_TYPE_P(obj, imemo_fields)) {
419+
size_t shape_id = rb_obj_shape_id(obj) & SHAPE_ID_OFFSET_MASK;
419420
dump_append(dc, ", \"shape_id\":");
420421
dump_append_sizet(dc, shape_id);
421422
}

gc.c

Lines changed: 37 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,49 +2015,39 @@ 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)) {
20562044
RUBY_ASSERT(FIXNUM_P(obj_id) || RB_TYPE_P(obj_id, T_BIGNUM));
20572045

20582046
if (!st_delete(id2ref_tbl, (st_data_t *)&obj_id, NULL)) {
2059-
// If we're currently building the table then it's not a bug
2060-
if (id2ref_tbl_built) {
2047+
// If we're currently building the table then it's not a bug.
2048+
// The the object is a T_IMEMO/fields, then it's possible the actual object
2049+
// has been garbage collected already.
2050+
if (id2ref_tbl_built && !RB_TYPE_P(obj, T_IMEMO)) {
20612051
rb_bug("Object ID seen, but not in _id2ref table: object_id=%llu object=%s", NUM2ULL(obj_id), rb_obj_info(obj));
20622052
}
20632053
}
@@ -2071,7 +2061,7 @@ rb_gc_obj_free_vm_weak_references(VALUE obj)
20712061
obj_free_object_id(obj);
20722062

20732063
if (rb_obj_exivar_p(obj)) {
2074-
rb_free_generic_ivar((VALUE)obj);
2064+
rb_free_generic_ivar(obj);
20752065
}
20762066

20772067
switch (BUILTIN_TYPE(obj)) {
@@ -2316,10 +2306,6 @@ rb_obj_memsize_of(VALUE obj)
23162306
return 0;
23172307
}
23182308

2319-
if (rb_obj_exivar_p(obj)) {
2320-
size += rb_generic_ivar_memsize(obj);
2321-
}
2322-
23232309
switch (BUILTIN_TYPE(obj)) {
23242310
case T_OBJECT:
23252311
if (rb_shape_obj_too_complex_p(obj)) {
@@ -3935,38 +3921,6 @@ vm_weak_table_foreach_update_weak_value(st_data_t *key, st_data_t *value, st_dat
39353921
return iter_data->update_callback((VALUE *)value, iter_data->data);
39363922
}
39373923

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

39723926
static int
@@ -4003,60 +3957,50 @@ vm_weak_table_gen_fields_foreach(st_data_t key, st_data_t value, st_data_t data)
40033957

40043958
int ret = iter_data->callback((VALUE)key, iter_data->data);
40053959

3960+
VALUE new_value = (VALUE)value;
3961+
VALUE new_key = (VALUE)key;
3962+
40063963
switch (ret) {
40073964
case ST_CONTINUE:
40083965
break;
40093966

40103967
case ST_DELETE:
4011-
free_gen_fields_tbl((VALUE)key, (struct gen_fields_tbl *)value);
40123968
RBASIC_SET_SHAPE_ID((VALUE)key, ROOT_SHAPE_ID);
40133969
return ST_DELETE;
40143970

40153971
case ST_REPLACE: {
4016-
VALUE new_key = (VALUE)key;
40173972
ret = iter_data->update_callback(&new_key, iter_data->data);
4018-
if (key != new_key) ret = ST_DELETE;
4019-
DURING_GC_COULD_MALLOC_REGION_START();
4020-
{
4021-
st_insert(rb_generic_fields_tbl_get(), (st_data_t)new_key, value);
3973+
if (key != new_key) {
3974+
ret = ST_DELETE;
40223975
}
4023-
DURING_GC_COULD_MALLOC_REGION_END();
4024-
key = (st_data_t)new_key;
40253976
break;
40263977
}
40273978

40283979
default:
4029-
return ret;
3980+
rb_bug("vm_weak_table_gen_fields_foreach: return value %d not supported", ret);
40303981
}
40313982

40323983
if (!iter_data->weak_only) {
4033-
struct gen_fields_tbl *fields_tbl = (struct gen_fields_tbl *)value;
3984+
int ivar_ret = iter_data->callback(new_value, iter_data->data);
3985+
switch (ivar_ret) {
3986+
case ST_CONTINUE:
3987+
break;
40343988

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

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

40624006
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)