summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ractor.c36
-rw-r--r--spec/ruby/optional/capi/digest_spec.rb6
-rw-r--r--st.c9
-rw-r--r--test/ruby/test_object_id.rb31
-rw-r--r--test/ruby/test_variable.rb14
-rw-r--r--variable.c51
6 files changed, 66 insertions, 81 deletions
diff --git a/ractor.c b/ractor.c
index 3eedf59048..cce376c543 100644
--- a/ractor.c
+++ b/ractor.c
@@ -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')
diff --git a/st.c b/st.c
index f11e9efaf9..70da7daf83 100644
--- a/st.c
+++ b/st.c
@@ -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