diff options
-rw-r--r-- | ractor.c | 36 | ||||
-rw-r--r-- | spec/ruby/optional/capi/digest_spec.rb | 6 | ||||
-rw-r--r-- | st.c | 9 | ||||
-rw-r--r-- | test/ruby/test_object_id.rb | 31 | ||||
-rw-r--r-- | test/ruby/test_variable.rb | 14 | ||||
-rw-r--r-- | variable.c | 51 |
6 files changed, 66 insertions, 81 deletions
@@ -1358,24 +1358,24 @@ make_shareable_check_shareable(VALUE obj) } switch (TYPE(obj)) { - case T_IMEMO: - return traverse_skip; - case T_OBJECT: - { - // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline, - // as it would require to move the object content into an external buffer. - // This is only a problem for T_OBJECT, given other types have external fields and can do RCU. - // To avoid this issue, we proactively create the object_id. - shape_id_t shape_id = RBASIC_SHAPE_ID(obj); - attr_index_t capacity = RSHAPE_CAPACITY(shape_id); - attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id); - if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) { - rb_obj_id(obj); - } - } - break; - default: - break; + case T_IMEMO: + return traverse_skip; + case T_OBJECT: + { + // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline, + // as it would require to move the object content into an external buffer. + // This is only a problem for T_OBJECT, given other types have external fields and can do RCU. + // To avoid this issue, we proactively create the object_id. + shape_id_t shape_id = RBASIC_SHAPE_ID(obj); + attr_index_t capacity = RSHAPE_CAPACITY(shape_id); + attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id); + if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) { + rb_obj_id(obj); + } + } + break; + default: + break; } if (!RB_OBJ_FROZEN_RAW(obj)) { diff --git a/spec/ruby/optional/capi/digest_spec.rb b/spec/ruby/optional/capi/digest_spec.rb index c753733906..65c5ecebb1 100644 --- a/spec/ruby/optional/capi/digest_spec.rb +++ b/spec/ruby/optional/capi/digest_spec.rb @@ -1,6 +1,10 @@ require_relative 'spec_helper' -require 'fiddle' +begin + require 'fiddle' +rescue LoadError + return +end load_extension('digest') @@ -1495,7 +1495,16 @@ st_update(st_table *tab, st_data_t key, value = entry->record; } old_key = key; + + unsigned int rebuilds_num = tab->rebuilds_num; + retval = (*func)(&key, &value, arg, existing); + + // We need to make sure that the callback didn't cause a table rebuild + // Ideally we would make sure no operations happened + assert(rebuilds_num == tab->rebuilds_num); + (void)rebuilds_num; + switch (retval) { case ST_CONTINUE: if (! existing) { diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index 018cc81496..9c0099517b 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -243,35 +243,4 @@ class TestObjectIdRactor < Test::Unit::TestCase assert_equal object_id, obj.object_id end; end - - def test_object_id_race_free_with_stress_compact - assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") - begin; - Warning[:experimental] = false - class MyClass - attr_reader :a, :b, :c - def initialize - @a = @b = @c = nil - end - end - N = 20 - objs = Ractor.make_shareable(N.times.map { MyClass.new }) - - GC.stress = true - GC.auto_compact = true if GC.respond_to?(:auto_compact=) - - results = 4.times.map{ - Ractor.new(objs) { |objs| - vars = [] - ids = [] - objs.each do |obj| - vars << obj.a << obj.b << obj.c - ids << obj.object_id - end - [vars, ids] - } - }.map(&:value) - assert_equal 1, results.uniq.size - end; - end end diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index 49fec2d40e..984045e05d 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -407,6 +407,20 @@ class TestVariable < Test::Unit::TestCase } end + def test_exivar_resize_with_compaction_stress + omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 + objs = 10_000.times.map do + ExIvar.new + end + EnvUtil.under_gc_compact_stress do + 10.times do + x = ExIvar.new + x.instance_variable_set(:@resize, 1) + x + end + end + end + def test_local_variables_with_kwarg bug11674 = '[ruby-core:71437] [Bug #11674]' v = with_kwargs_11(v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8,v9:9,v10:10,v11:11) diff --git a/variable.c b/variable.c index 67dc2d3397..6bd9f69d06 100644 --- a/variable.c +++ b/variable.c @@ -1818,51 +1818,40 @@ general_field_set(VALUE obj, shape_id_t target_shape_id, VALUE val, void *data, struct gen_fields_lookup_ensure_size { VALUE obj; ID id; - struct gen_fields_tbl *fields_tbl; shape_id_t shape_id; bool resize; }; -static int -generic_fields_lookup_ensure_size(st_data_t *k, st_data_t *v, st_data_t u, int existing) -{ - ASSERT_vm_locking(); - - struct gen_fields_lookup_ensure_size *fields_lookup = (struct gen_fields_lookup_ensure_size *)u; - struct gen_fields_tbl *fields_tbl = existing ? (struct gen_fields_tbl *)*v : NULL; - - if (!existing || fields_lookup->resize) { - if (existing) { - RUBY_ASSERT(RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_OBJ_ID)); - RUBY_ASSERT(RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)) < RSHAPE_CAPACITY(fields_lookup->shape_id)); - } - - fields_tbl = gen_fields_tbl_resize(fields_tbl, RSHAPE_CAPACITY(fields_lookup->shape_id)); - *v = (st_data_t)fields_tbl; - } - - fields_lookup->fields_tbl = fields_tbl; - if (fields_lookup->shape_id) { - rb_obj_set_shape_id(fields_lookup->obj, fields_lookup->shape_id); - } - - RUBY_ASSERT(rb_obj_exivar_p((VALUE)*k)); - - return ST_CONTINUE; -} - static VALUE * generic_ivar_set_shape_fields(VALUE obj, void *data) { RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); struct gen_fields_lookup_ensure_size *fields_lookup = data; + struct gen_fields_tbl *fields_tbl = NULL; + // We can't use st_update, since when resizing the fields table GC can + // happen, which will modify the st_table and may rebuild it RB_VM_LOCKING() { - st_update(generic_fields_tbl(obj, fields_lookup->id, false), (st_data_t)obj, generic_fields_lookup_ensure_size, (st_data_t)fields_lookup); + st_table *tbl = generic_fields_tbl(obj, fields_lookup->id, false); + int existing = st_lookup(tbl, (st_data_t)obj, (st_data_t *)&fields_tbl); + + if (!existing || fields_lookup->resize) { + if (existing) { + RUBY_ASSERT(RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_IVAR) || RSHAPE_TYPE_P(fields_lookup->shape_id, SHAPE_OBJ_ID)); + RUBY_ASSERT(RSHAPE_CAPACITY(RSHAPE_PARENT(fields_lookup->shape_id)) < RSHAPE_CAPACITY(fields_lookup->shape_id)); + } + + fields_tbl = gen_fields_tbl_resize(fields_tbl, RSHAPE_CAPACITY(fields_lookup->shape_id)); + st_insert(tbl, (st_data_t)obj, (st_data_t)fields_tbl); + } + + if (fields_lookup->shape_id) { + rb_obj_set_shape_id(fields_lookup->obj, fields_lookup->shape_id); + } } - return fields_lookup->fields_tbl->as.shape.fields; + return fields_tbl->as.shape.fields; } static void |