diff options
| author | John Hawthorn <john@hawthorn.email> | 2025-11-26 14:02:17 -0800 |
|---|---|---|
| committer | John Hawthorn <john@hawthorn.email> | 2025-11-26 16:13:38 -0800 |
| commit | 5bef7577b3b336a709935dd39e69abcf397c4684 (patch) | |
| tree | 72dd8d38a41989c9615dbc205dd0892b11d6a212 | |
| parent | 970b18e9a94ff3e6496fb7324cff0798ffec6f24 (diff) | |
Ensure we don't dereference fields_obj as Qundef
We rely on the GC to clear this when the GC is run on another EC than
the cache.
| -rw-r--r-- | test/ruby/test_variable.rb | 16 | ||||
| -rw-r--r-- | variable.c | 4 |
2 files changed, 19 insertions, 1 deletions
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index d4ba0f9fc7..13b8a7905f 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -529,6 +529,22 @@ class TestVariable < Test::Unit::TestCase assert_equal :new_value, str.instance_variable_get(:@x) end + def test_genivar_cache_invalidated_by_gc + str = +"hello" + str.instance_variable_set(:@x, :old_value) + + str.instance_variable_get(:@x) # populate cache + + Fiber.new { + str.remove_instance_variable(:@x) + str.instance_variable_set(:@x, :new_value) + }.resume + + GC.start + + assert_equal :new_value, str.instance_variable_get(:@x) + end + private def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:) local_variables diff --git a/variable.c b/variable.c index ae122136ee..f7ee561220 100644 --- a/variable.c +++ b/variable.c @@ -1273,7 +1273,7 @@ rb_obj_fields(VALUE obj, ID field_name) generic_fields: { rb_execution_context_t *ec = GET_EC(); - if (ec->gen_fields_cache.obj == obj && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) { + if (ec->gen_fields_cache.obj == obj && !UNDEF_P(ec->gen_fields_cache.fields_obj) && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) { fields_obj = ec->gen_fields_cache.fields_obj; RUBY_ASSERT(fields_obj == rb_obj_fields_generic_uncached(obj)); } @@ -1309,6 +1309,8 @@ rb_free_generic_ivar(VALUE obj) default: generic_fields: { + // Other EC may have stale caches, so fields_obj should be + // invalidated and the GC will replace with Qundef rb_execution_context_t *ec = GET_EC(); if (ec->gen_fields_cache.obj == obj) { ec->gen_fields_cache.obj = Qundef; |
