Skip to content

Commit a4f4af8

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 6d4fe59 commit a4f4af8

File tree

10 files changed

+350
-400
lines changed

10 files changed

+350
-400
lines changed

ext/objspace/objspace_dump.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ 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;
399399
} else {
400400
dc->cur_obj_klass = RBASIC_CLASS(obj);
@@ -414,8 +414,8 @@ dump_object(VALUE obj, struct dump_config *dc)
414414
dump_append(dc, obj_type(obj));
415415
dump_append(dc, "\"");
416416

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

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy