diff options
author | John Hawthorn <[email protected]> | 2025-03-18 15:02:44 -0700 |
---|---|---|
committer | John Hawthorn <[email protected]> | 2025-03-20 13:09:40 -0700 |
commit | bfe6068417ca41a6b88a1ba5fcde04f9a76718a7 (patch) | |
tree | be79cd8f9a3105f0eff1fb5f8d086e979e7de323 | |
parent | 62cc3464d902ee7a399ec8c38606fdc0ee98f05e (diff) |
Use atomic for method reference count [Bug #20934]
This changes reference_count on rb_method_definition_struct into an
atomic.
Ractors can create additional references as part of `bind_call` or
(presumably) similar. Because this can be done inside Ractors, we should
use a lock or atomics so that we don't race and avoid incrementing.
Co-authored-by: wanabe <[email protected]>
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/12951
-rw-r--r-- | bootstraptest/test_ractor.rb | 13 | ||||
-rw-r--r-- | method.h | 4 | ||||
-rw-r--r-- | vm_method.c | 23 |
3 files changed, 29 insertions, 11 deletions
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 55dae42111..b296ad0331 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1937,3 +1937,16 @@ assert_equal 'LoadError', %q{ end r.take } + +# bind_call in Ractor [Bug #20934] +assert_equal 'ok', %q{ + 2.times.map do + Ractor.new do + 1000.times do + Object.instance_method(:itself).bind_call(self) + end + end + end.each(&:take) + GC.start + :ok.itself +} @@ -15,6 +15,7 @@ #include "internal/imemo.h" #include "internal/compilers.h" #include "internal/static_assert.h" +#include "ruby/atomic.h" #ifndef END_OF_ENUMERATION # if defined(__GNUC__) &&! defined(__STRICT_ANSI__) @@ -181,7 +182,8 @@ struct rb_method_definition_struct { unsigned int iseq_overload: 1; unsigned int no_redef_warning: 1; unsigned int aliased : 1; - int reference_count : 28; + + rb_atomic_t reference_count; union { rb_method_iseq_t iseq; diff --git a/vm_method.c b/vm_method.c index 205bf0b552..95a252337f 100644 --- a/vm_method.c +++ b/vm_method.c @@ -511,14 +511,13 @@ static void rb_method_definition_release(rb_method_definition_t *def) { if (def != NULL) { - const int reference_count = def->reference_count; - def->reference_count--; + const unsigned int reference_count_was = RUBY_ATOMIC_FETCH_SUB(def->reference_count, 1); - VM_ASSERT(reference_count >= 0); + RUBY_ASSERT_ALWAYS(reference_count_was != 0); - if (def->reference_count == 0) { - if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d (remove)\n", (void *)def, - rb_id2name(def->original_id), def->reference_count); + if (reference_count_was == 1) { + if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:1->0 (remove)\n", (void *)def, + rb_id2name(def->original_id)); if (def->type == VM_METHOD_TYPE_BMETHOD && def->body.bmethod.hooks) { xfree(def->body.bmethod.hooks); } @@ -526,7 +525,7 @@ rb_method_definition_release(rb_method_definition_t *def) } else { if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id), - reference_count, def->reference_count); + reference_count_was, reference_count_was - 1); } } } @@ -614,9 +613,13 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(ANYARGS), int static rb_method_definition_t * method_definition_addref(rb_method_definition_t *def, bool complemented) { - if (!complemented && def->reference_count > 0) def->aliased = true; - def->reference_count++; - if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->reference_count); + unsigned int reference_count_was = RUBY_ATOMIC_FETCH_ADD(def->reference_count, 1); + if (!complemented && reference_count_was > 0) { + /* TODO: A Ractor can reach this via UnboundMethod#bind */ + def->aliased = true; + } + if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d->%d\n", (void *)def, rb_id2name(def->original_id), reference_count_was, reference_count_was+1); + return def; } |