Skip to content

Commit bfe6068

Browse files
jhawthornwanabe
andcommitted
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]>
1 parent 62cc346 commit bfe6068

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

bootstraptest/test_ractor.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,3 +1937,16 @@ def require feature
19371937
end
19381938
r.take
19391939
}
1940+
1941+
# bind_call in Ractor [Bug #20934]
1942+
assert_equal 'ok', %q{
1943+
2.times.map do
1944+
Ractor.new do
1945+
1000.times do
1946+
Object.instance_method(:itself).bind_call(self)
1947+
end
1948+
end
1949+
end.each(&:take)
1950+
GC.start
1951+
:ok.itself
1952+
}

method.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "internal/imemo.h"
1616
#include "internal/compilers.h"
1717
#include "internal/static_assert.h"
18+
#include "ruby/atomic.h"
1819

1920
#ifndef END_OF_ENUMERATION
2021
# if defined(__GNUC__) &&! defined(__STRICT_ANSI__)
@@ -181,7 +182,8 @@ struct rb_method_definition_struct {
181182
unsigned int iseq_overload: 1;
182183
unsigned int no_redef_warning: 1;
183184
unsigned int aliased : 1;
184-
int reference_count : 28;
185+
186+
rb_atomic_t reference_count;
185187

186188
union {
187189
rb_method_iseq_t iseq;

vm_method.c

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -511,22 +511,21 @@ static void
511511
rb_method_definition_release(rb_method_definition_t *def)
512512
{
513513
if (def != NULL) {
514-
const int reference_count = def->reference_count;
515-
def->reference_count--;
514+
const unsigned int reference_count_was = RUBY_ATOMIC_FETCH_SUB(def->reference_count, 1);
516515

517-
VM_ASSERT(reference_count >= 0);
516+
RUBY_ASSERT_ALWAYS(reference_count_was != 0);
518517

519-
if (def->reference_count == 0) {
520-
if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d (remove)\n", (void *)def,
521-
rb_id2name(def->original_id), def->reference_count);
518+
if (reference_count_was == 1) {
519+
if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:1->0 (remove)\n", (void *)def,
520+
rb_id2name(def->original_id));
522521
if (def->type == VM_METHOD_TYPE_BMETHOD && def->body.bmethod.hooks) {
523522
xfree(def->body.bmethod.hooks);
524523
}
525524
xfree(def);
526525
}
527526
else {
528527
if (METHOD_DEBUG) fprintf(stderr, "-%p-%s:%d->%d (dec)\n", (void *)def, rb_id2name(def->original_id),
529-
reference_count, def->reference_count);
528+
reference_count_was, reference_count_was - 1);
530529
}
531530
}
532531
}
@@ -614,9 +613,13 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(ANYARGS), int
614613
static rb_method_definition_t *
615614
method_definition_addref(rb_method_definition_t *def, bool complemented)
616615
{
617-
if (!complemented && def->reference_count > 0) def->aliased = true;
618-
def->reference_count++;
619-
if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d\n", (void *)def, rb_id2name(def->original_id), def->reference_count);
616+
unsigned int reference_count_was = RUBY_ATOMIC_FETCH_ADD(def->reference_count, 1);
617+
if (!complemented && reference_count_was > 0) {
618+
/* TODO: A Ractor can reach this via UnboundMethod#bind */
619+
def->aliased = true;
620+
}
621+
if (METHOD_DEBUG) fprintf(stderr, "+%p-%s:%d->%d\n", (void *)def, rb_id2name(def->original_id), reference_count_was, reference_count_was+1);
622+
620623
return def;
621624
}
622625

0 commit comments

Comments
 (0)