Skip to content

Commit bfd28d5

Browse files
authored
make rb_singleton_class ractor safe (ruby#15591)
Since singleton classes are created lazily, we need to make sure that we lock around their creation. Unfortunately, that means we need to lock around every shareable object's call to `singleton_class`, including classes and modules.
1 parent f133ebb commit bfd28d5

File tree

3 files changed

+39
-14
lines changed

3 files changed

+39
-14
lines changed

class.c

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "internal/variable.h"
3131
#include "ruby/st.h"
3232
#include "vm_core.h"
33+
#include "ruby/ractor.h"
3334
#include "yjit.h"
3435
#include "zjit.h"
3536

@@ -2823,7 +2824,7 @@ rb_special_singleton_class(VALUE obj)
28232824
* consistency of the metaclass hierarchy.
28242825
*/
28252826
static VALUE
2826-
singleton_class_of(VALUE obj)
2827+
singleton_class_of(VALUE obj, bool ensure_eigenclass)
28272828
{
28282829
VALUE klass;
28292830

@@ -2851,13 +2852,26 @@ singleton_class_of(VALUE obj)
28512852
}
28522853
}
28532854

2854-
klass = METACLASS_OF(obj);
2855-
if (!(RCLASS_SINGLETON_P(klass) &&
2856-
RCLASS_ATTACHED_OBJECT(klass) == obj)) {
2857-
klass = rb_make_metaclass(obj, klass);
2855+
bool needs_lock = rb_multi_ractor_p() && rb_ractor_shareable_p(obj);
2856+
unsigned int lev;
2857+
if (needs_lock) {
2858+
RB_VM_LOCK_ENTER_LEV(&lev);
2859+
}
2860+
{
2861+
klass = METACLASS_OF(obj);
2862+
if (!(RCLASS_SINGLETON_P(klass) &&
2863+
RCLASS_ATTACHED_OBJECT(klass) == obj)) {
2864+
klass = rb_make_metaclass(obj, klass);
2865+
}
2866+
RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
2867+
if (ensure_eigenclass && RB_TYPE_P(obj, T_CLASS)) {
2868+
/* ensures an exposed class belongs to its own eigenclass */
2869+
(void)ENSURE_EIGENCLASS(klass);
2870+
}
2871+
}
2872+
if (needs_lock) {
2873+
RB_VM_LOCK_LEAVE_LEV(&lev);
28582874
}
2859-
2860-
RB_FL_SET_RAW(klass, RB_OBJ_FROZEN_RAW(obj));
28612875

28622876
return klass;
28632877
}
@@ -2900,12 +2914,7 @@ rb_singleton_class_get(VALUE obj)
29002914
VALUE
29012915
rb_singleton_class(VALUE obj)
29022916
{
2903-
VALUE klass = singleton_class_of(obj);
2904-
2905-
/* ensures an exposed class belongs to its own eigenclass */
2906-
if (RB_TYPE_P(obj, T_CLASS)) (void)ENSURE_EIGENCLASS(klass);
2907-
2908-
return klass;
2917+
return singleton_class_of(obj, true);
29092918
}
29102919

29112920
/*!
@@ -2923,7 +2932,7 @@ rb_singleton_class(VALUE obj)
29232932
void
29242933
rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), int argc)
29252934
{
2926-
rb_define_method(singleton_class_of(obj), name, func, argc);
2935+
rb_define_method(singleton_class_of(obj, false), name, func, argc);
29272936
}
29282937

29292938
#ifdef rb_define_module_function

depend

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ class.$(OBJEXT): {$(VPATH)}missing.h
14031403
class.$(OBJEXT): {$(VPATH)}node.h
14041404
class.$(OBJEXT): {$(VPATH)}onigmo.h
14051405
class.$(OBJEXT): {$(VPATH)}oniguruma.h
1406+
class.$(OBJEXT): {$(VPATH)}ractor.h
14061407
class.$(OBJEXT): {$(VPATH)}ruby_assert.h
14071408
class.$(OBJEXT): {$(VPATH)}ruby_atomic.h
14081409
class.$(OBJEXT): {$(VPATH)}rubyparser.h

test/ruby/test_class.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,4 +930,19 @@ def test_safe_multi_ractor_subclasses_list_mutation
930930
end.each(&:join)
931931
end;
932932
end
933+
934+
def test_safe_multi_ractor_singleton_class_access
935+
assert_ractor "#{<<~"begin;"}\n#{<<~'end;'}"
936+
begin;
937+
class A; end
938+
4.times.map do
939+
Ractor.new do
940+
a = A
941+
100.times do
942+
a = a.singleton_class
943+
end
944+
end
945+
end.each(&:join)
946+
end;
947+
end
933948
end

0 commit comments

Comments
 (0)