summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hawthorn <[email protected]>2025-03-18 15:02:44 -0700
committerJohn Hawthorn <[email protected]>2025-03-20 13:09:40 -0700
commitbfe6068417ca41a6b88a1ba5fcde04f9a76718a7 (patch)
treebe79cd8f9a3105f0eff1fb5f8d086e979e7de323
parent62cc3464d902ee7a399ec8c38606fdc0ee98f05e (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.rb13
-rw-r--r--method.h4
-rw-r--r--vm_method.c23
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
+}
diff --git a/method.h b/method.h
index 030c3cc3d1..99ee922335 100644
--- a/method.h
+++ b/method.h
@@ -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;
}