summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hawthorn <john@hawthorn.email>2025-11-26 14:02:17 -0800
committerJohn Hawthorn <john@hawthorn.email>2025-11-26 16:13:38 -0800
commit5bef7577b3b336a709935dd39e69abcf397c4684 (patch)
tree72dd8d38a41989c9615dbc205dd0892b11d6a212
parent970b18e9a94ff3e6496fb7324cff0798ffec6f24 (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.rb16
-rw-r--r--variable.c4
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;