ruby-changes:63395
From: Koichi <ko1@a...>
Date: Wed, 21 Oct 2020 07:59:43 +0900 (JST)
Subject: [ruby-changes:63395] 2f50936cb9 (master): Ractor.make_shareable(obj)
https://git.ruby-lang.org/ruby.git/commit/?id=2f50936cb9 From 2f50936cb913b7458cbaa03dc4652f1127a7631a Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Wed, 21 Oct 2020 00:54:03 +0900 Subject: Ractor.make_shareable(obj) Introduce new method Ractor.make_shareable(obj) which tries to make obj shareable object. Protocol is here. (1) If obj is shareable, it is shareable. (2) If obj is not a shareable object and if obj can be shareable object if it is frozen, then freeze obj. If obj has reachable objects (rs), do rs.each{|o| Ractor.make_shareable(o)} recursively (recursion is not Ruby-level, but C-level). (3) Otherwise, raise Ractor::Error. Now T_DATA is not a shareable object even if the object is frozen. If the method finished without error, given obj is marked as a sharable object. To allow makng a shareable frozen T_DATA object, then set `RUBY_TYPED_FROZEN_SHAREABLE` as type->flags. On default, this flag is not set. It means user defined T_DATA objects are not allowed to become shareable objects when it is frozen. You can make any object shareable by setting FL_SHAREABLE flag, so if you know that the T_DATA object is shareable (== thread-safe), set this flag, at creation time for example. `Ractor` object is one example, which is not a frozen, but a shareable object. diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index f951d4b..6290b73 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -832,6 +832,80 @@ assert_equal '0', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_ractor.rb#L832 }.take } +# Ractor.make_shareable(obj) +assert_equal 'true', %q{ + class C + def initialize + @a = 'foo' + @b = 'bar' + end + attr_reader :a, :b + end + S = Struct.new(:s1, :s2) + str = "hello" + str.instance_variable_set("@iv", "hello") + /a/ =~ 'a' + m = $~ + class N < Numeric + def /(other) + 1 + end + end + ary = []; ary << ary + + a = [[1, ['2', '3']], + {Object.new => "hello"}, + C.new, + S.new("x", "y"), + ("a".."b"), + str, + ary, # cycle + /regexp/, + /#{'r'.upcase}/, + m, + Complex(N.new,0), + Rational(N.new,0), + true, + false, + nil, + 1, 1.2, 1+3r, 1+4i, # Numeric + ] + Ractor.make_shareable(a) + + # check all frozen + a.each{|o| + raise o.inspect unless o.frozen? + + case o + when C + raise o.a.inspect unless o.a.frozen? + raise o.b.inspect unless o.b.frozen? + when Rational + raise o.numerator.inspect unless o.numerator.frozen? + when Complex + raise o.real.inspect unless o.real.frozen? + when Array + if o[0] == 1 + raise o[1][1].inspect unless o[1][1].frozen? + end + when Hash + o.each{|k, v| + raise k.inspect unless k.frozen? + raise v.inspect unless v.frozen? + } + end + } + + Ractor.shareable?(a) +} + +# Ractor.make_shareable(obj) doesn't freeze shareable objects +assert_equal 'true', %q{ + r = Ractor.new{} + Ractor.make_shareable(a = [r]) + [a.frozen?, a[0].frozen?] == [true, false] +} + ### ### Synchronization tests ### diff --git a/common.mk b/common.mk index a8719ca..7b5ab6f 100644 --- a/common.mk +++ b/common.mk @@ -10193,10 +10193,17 @@ ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h https://github.com/ruby/ruby/blob/trunk/common.mk#L10193 ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h ractor.$(OBJEXT): $(top_srcdir)/internal/array.h +ractor.$(OBJEXT): $(top_srcdir)/internal/bignum.h +ractor.$(OBJEXT): $(top_srcdir)/internal/bits.h ractor.$(OBJEXT): $(top_srcdir)/internal/compilers.h +ractor.$(OBJEXT): $(top_srcdir)/internal/complex.h ractor.$(OBJEXT): $(top_srcdir)/internal/error.h +ractor.$(OBJEXT): $(top_srcdir)/internal/fixnum.h ractor.$(OBJEXT): $(top_srcdir)/internal/gc.h +ractor.$(OBJEXT): $(top_srcdir)/internal/hash.h ractor.$(OBJEXT): $(top_srcdir)/internal/imemo.h +ractor.$(OBJEXT): $(top_srcdir)/internal/numeric.h +ractor.$(OBJEXT): $(top_srcdir)/internal/rational.h ractor.$(OBJEXT): $(top_srcdir)/internal/serial.h ractor.$(OBJEXT): $(top_srcdir)/internal/static_assert.h ractor.$(OBJEXT): $(top_srcdir)/internal/string.h @@ -10220,6 +10227,7 @@ ractor.$(OBJEXT): {$(VPATH)}debug.h https://github.com/ruby/ruby/blob/trunk/common.mk#L10227 ractor.$(OBJEXT): {$(VPATH)}debug_counter.h ractor.$(OBJEXT): {$(VPATH)}defines.h ractor.$(OBJEXT): {$(VPATH)}encoding.h +ractor.$(OBJEXT): {$(VPATH)}gc.h ractor.$(OBJEXT): {$(VPATH)}id.h ractor.$(OBJEXT): {$(VPATH)}id_table.h ractor.$(OBJEXT): {$(VPATH)}intern.h @@ -10381,6 +10389,7 @@ ractor.$(OBJEXT): {$(VPATH)}subst.h https://github.com/ruby/ruby/blob/trunk/common.mk#L10389 ractor.$(OBJEXT): {$(VPATH)}thread.h ractor.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h ractor.$(OBJEXT): {$(VPATH)}thread_native.h +ractor.$(OBJEXT): {$(VPATH)}variable.h ractor.$(OBJEXT): {$(VPATH)}vm_core.h ractor.$(OBJEXT): {$(VPATH)}vm_debug.h ractor.$(OBJEXT): {$(VPATH)}vm_opts.h diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index 3ffe07e..c038e6f 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -52,6 +52,7 @@ https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/core/rtypeddata.h#L52 #define RTYPEDDATA_P RTYPEDDATA_P #define RTYPEDDATA_TYPE RTYPEDDATA_TYPE #define RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY +#define RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE #define RUBY_TYPED_WB_PROTECTED RUBY_TYPED_WB_PROTECTED #define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1 /** @endcond */ @@ -59,6 +60,7 @@ https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/core/rtypeddata.h#L60 /* bits for rb_data_type_struct::flags */ enum rbimpl_typeddata_flags { RUBY_TYPED_FREE_IMMEDIATELY = 1, + RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE, RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */ RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1 /* THIS FLAG DEPENDS ON Ruby version */ }; diff --git a/internal/hash.h b/internal/hash.h index 237ce58..a4677c5 100644 --- a/internal/hash.h +++ b/internal/hash.h @@ -83,6 +83,8 @@ VALUE rb_hash_set_pair(VALUE hash, VALUE pair); https://github.com/ruby/ruby/blob/trunk/internal/hash.h#L83 int rb_hash_stlike_delete(VALUE hash, st_data_t *pkey, st_data_t *pval); int rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg); int rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func, st_data_t arg); +extern st_table *rb_hash_st_table(VALUE hash); + static inline unsigned RHASH_AR_TABLE_SIZE_RAW(VALUE h); static inline VALUE RHASH_IFNONE(VALUE h); static inline size_t RHASH_SIZE(VALUE h); @@ -135,7 +137,6 @@ RHASH_AR_TABLE(VALUE h) https://github.com/ruby/ruby/blob/trunk/internal/hash.h#L137 static inline st_table * RHASH_ST_TABLE(VALUE h) { - extern st_table *rb_hash_st_table(VALUE hash); return rb_hash_st_table(h) } diff --git a/ractor.c b/ractor.c index 68ac5a2..8c498a8 100644 --- a/ractor.c +++ b/ractor.c @@ -6,8 +6,13 @@ https://github.com/ruby/ruby/blob/trunk/ractor.c#L6 #include "vm_core.h" #include "vm_sync.h" #include "ractor.h" +#include "internal/complex.h" #include "internal/error.h" +#include "internal/hash.h" +#include "internal/rational.h" #include "internal/struct.h" +#include "variable.h" +#include "gc.h" static VALUE rb_cRactor; static VALUE rb_eRactorError; @@ -1743,8 +1748,6 @@ rb_vm_main_ractor_ec(rb_vm_t *vm) https://github.com/ruby/ruby/blob/trunk/ractor.c#L1748 return vm->ractor.main_ractor->threads.running_ec; } -#include "ractor.rbinc" - static VALUE ractor_moved_missing(int argc, VALUE *argv, VALUE self) { @@ -1777,128 +1780,6 @@ Init_Ractor(void) https://github.com/ruby/ruby/blob/trunk/ractor.c#L1780 rb_obj_freeze(rb_cRactorMovedObject); } -static int -rb_ractor_shareable_p_hash_i(VALUE key, VALUE value, VALUE arg) -{ - // TODO: should we need to avoid recursion to prevent stack overflow? - if (!rb_ractor_shareable_p(key) || !rb_ractor_shareable_p(value)) { - bool *shareable = (bool*)arg; - *shareable = false; - return ST_STOP; - } - return ST_CONTINUE; -} - -static bool -ractor_struct_shareable_members_p(VALUE obj) -{ - VM_ASSERT(RB_TYPE_P(obj, T_STRUCT)); - - long len = RSTRUCT_LEN(obj); - const VALUE *ptr = RSTRUCT_CONST_PTR(obj); - - for (long i=0; i<len; i++) { - if (!rb_ractor_shareable_p(ptr[i])) { - return false; - } - } - return true; -} - -static bool -ractor_obj_ivars_shareable_p(VALUE obj) -{ - uint32_t len = ROBJECT_NUMIV(obj); - VALUE *ptr = ROBJECT_IVPTR(obj); - - for (uint32_t i=0; i<len; i++) { - VALUE val = ptr[i]; - if (val != Qundef && !rb_ractor_shareable_p(ptr[i])) { - return false; - } - } - - return true; -} - -MJIT_FUNC_EXPORTED bool -rb_ractor_shareable_p_continue(VALUE obj) -{ - switch (BUILTIN_TYPE(obj)) { - case T_CLASS: - case T_MODULE: - case T_ICLASS: - goto shareable; - - case T_FLOAT: - case T_COMPLEX: - case T_RATIONAL: - case T_BIGNUM: - case T_SYMBOL: - VM_ASSERT(RB_OBJ_FROZEN_RAW(obj)); - goto shareable; - - case T_STRING: - case T_REGEXP: - if (RB_OBJ_FROZEN_RAW(obj) && - !FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - goto shareable; - } - return false; - case T_ARRAY: - if (!RB_OBJ_FROZEN_RAW(obj) || - FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - return false; - } - else { - for (int i = 0; i < RARRAY_LEN(obj); i++) { - if (!rb_ractor_shareable_p(rb_ary_entry(obj, i))) r (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/