ruby-changes:64541
From: Koichi <ko1@a...>
Date: Thu, 24 Dec 2020 14:29:07 +0900 (JST)
Subject: [ruby-changes:64541] 6f29716f9f (master): shareable_constant_value: experimental_copy
https://git.ruby-lang.org/ruby.git/commit/?id=6f29716f9f From 6f29716f9ffb710af7f344839ec67ef2b8a48ab2 Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Thu, 24 Dec 2020 10:59:27 +0900 Subject: shareable_constant_value: experimental_copy "experimental_everything" makes the assigned value, it means the assignment change the state of assigned value. "experimental_copy" tries to make a deep copy and make copyied object sharable. diff --git a/doc/syntax/comments.rdoc b/doc/syntax/comments.rdoc index 6ce8fa1..a808ca8 100644 --- a/doc/syntax/comments.rdoc +++ b/doc/syntax/comments.rdoc @@ -125,6 +125,7 @@ The directive can specify special treatment for values assigned to constants: https://github.com/ruby/ruby/blob/trunk/doc/syntax/comments.rdoc#L125 * +none+: (default) * +literal+: literals are implicitly frozen, others must be Ractor-shareable * +experimental_everything+: all made shareable +* +experimental_copy+: copy deeply and make it shareable ==== Mode +none+ (default) @@ -168,22 +169,46 @@ The method Module#const_set is not affected. https://github.com/ruby/ruby/blob/trunk/doc/syntax/comments.rdoc#L169 In this mode, all values assigned to constants are made shareable. # shareable_constant_value: experimental_everything - FOO = Set.new[1, 2, {foo: []}] # => ok, since this is - # same as `Set.new[1, 2, {foo: [].freeze}.freeze].freeze` + FOO = Set[1, 2, {foo: []}] + # same as FOO = Ractor.make_sharable(...) + # OR same as `FOO = Set[1, 2, {foo: [].freeze}.freeze].freeze` var = [{foo: []}] var.frozen? # => false (assignment was made to local variable) X = var # => calls `Ractor.make_shareable(var)` var.frozen? # => true -This mode is "experimental", because it might be too error prone, -for example by deep-freezing the constants of an external resource -which could cause errors: +This mode is "experimental", because it might be error prone, for +example by deep-freezing the constants of an external resource which +could cause errors: # shareable_constant_value: experimental_everything FOO = SomeGem::Something::FOO # => deep freezes the gem's constant! +We will revisit to consider removing "experimental_" or removing this +mode by checking usages before Ruby 3.1. + +The method Module#const_set is not affected. + +==== Mode +experimental_copy+ + +In this mode, all values assigned to constants are copyied deeply and +made shareable. It is safer mode than +experimental_everything+. + + # shareable_constant_value: experimental_everything + var = [{foo: []}] + var.frozen? # => false (assignment was made to local variable) + X = var # => calls `Ractor.make_shareable(var, copy: true)` + var.frozen? # => false + Ractor.shareable?(var) #=> false + Ractor.shareable?(X) #=> true + var.object_id == X.object_id #=> false + +This mode is "experimental", because it is not discussed enough. +We will revisit to consider removing "experimental_" or removing this +mode by checking usages before Ruby 3.1. + The method Module#const_set is not affected. ==== Scope diff --git a/include/ruby/ractor.h b/include/ruby/ractor.h index 327d4f4..239df9c 100644 --- a/include/ruby/ractor.h +++ b/include/ruby/ractor.h @@ -61,4 +61,7 @@ rb_ractor_shareable_p(VALUE obj) https://github.com/ruby/ruby/blob/trunk/include/ruby/ractor.h#L61 } } +VALUE rb_ractor_make_shareable(VALUE obj); +VALUE rb_ractor_make_shareable_copy(VALUE obj); + #endif /* RUBY_RACTOR_H */ diff --git a/parse.y b/parse.y index 570a400..fa70b1b 100644 --- a/parse.y +++ b/parse.y @@ -58,6 +58,7 @@ struct lex_context; https://github.com/ruby/ruby/blob/trunk/parse.y#L58 enum shareability { shareable_none, shareable_literal, + shareable_copy, shareable_everything, }; @@ -8053,6 +8054,10 @@ parser_set_shareable_constant_value(struct parser_params *p, const char *name, c https://github.com/ruby/ruby/blob/trunk/parse.y#L8054 } break; case 'e': case 'E': + if (STRCASECMP(val, "experimental_copy") == 0) { + p->ctxt.shareable_constant_value = shareable_copy; + return; + } if (STRCASECMP(val, "experimental_everything") == 0) { p->ctxt.shareable_constant_value = shareable_everything; return; @@ -11048,11 +11053,18 @@ const_decl_path(struct parser_params *p, NODE **dest) https://github.com/ruby/ruby/blob/trunk/parse.y#L11053 extern VALUE rb_mRubyVMFrozenCore; static NODE * -make_shareable_node(struct parser_params *p, NODE *value, const YYLTYPE *loc) +make_shareable_node(struct parser_params *p, NODE *value, bool copy, const YYLTYPE *loc) { NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc); - return NEW_CALL(fcore, rb_intern("make_shareable"), - NEW_LIST(value, loc), loc); + + if (copy) { + return NEW_CALL(fcore, rb_intern("make_shareable_copy"), + NEW_LIST(value, loc), loc); + } + else { + return NEW_CALL(fcore, rb_intern("make_shareable"), + NEW_LIST(value, loc), loc); + } } static NODE * @@ -11089,8 +11101,6 @@ shareable_literal_value(NODE *node) https://github.com/ruby/ruby/blob/trunk/parse.y#L11101 #define SHAREABLE_BARE_EXPRESSION 1 #endif -VALUE rb_ractor_make_shareable(VALUE obj); - static NODE * shareable_literal_constant(struct parser_params *p, enum shareability shareable, NODE **dest, NODE *value, const YYLTYPE *loc, size_t level) @@ -11207,7 +11217,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable, https://github.com/ruby/ruby/blob/trunk/parse.y#L11217 if (NIL_P(lit)) { // if shareable_literal, all elements should have been ensured // as shareable - value = make_shareable_node(p, value, loc); + value = make_shareable_node(p, value, false, loc); } else { nd_set_type(value, NODE_LIT); @@ -11235,11 +11245,12 @@ shareable_constant_value(struct parser_params *p, enum shareability shareable, https://github.com/ruby/ruby/blob/trunk/parse.y#L11245 } break; + case shareable_copy: case shareable_everything: { NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0); if (lit) return lit; - return make_shareable_node(p, value, loc); + return make_shareable_node(p, value, shareable == shareable_copy, loc); } break; diff --git a/ractor_core.h b/ractor_core.h index 0aa66f6..2516277 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -184,6 +184,8 @@ bool rb_ractor_main_p_(void); https://github.com/ruby/ruby/blob/trunk/ractor_core.h#L184 void rb_ractor_finish_marking(void); void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th); +VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name); + RUBY_SYMBOL_EXPORT_BEGIN bool rb_ractor_shareable_p_continue(VALUE obj); diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 2e3fbc9..5f638af 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1205,6 +1205,7 @@ x = __ENCODING__ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_parse.rb#L1205 end def test_shareable_constant_value_simple + obj = [['unsharable_value']] a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}") begin; # shareable_constant_value: experimental_everything @@ -1222,6 +1223,18 @@ x = __ENCODING__ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_parse.rb#L1223 assert_ractor_shareable(c) assert_equal([1], a[0]) assert_ractor_shareable(a[0]) + + a, obj = eval_separately(<<~'end;') + # shareable_constant_value: experimental_copy + obj = [["unshareable"]] + A = obj + [A, obj] + end; + + assert_ractor_shareable(a) + assert_not_ractor_shareable(obj) + assert_equal obj, a + assert !obj.equal?(a) end def test_shareable_constant_value_nested diff --git a/vm.c b/vm.c index a30ba9c..696cffb 100644 --- a/vm.c +++ b/vm.c @@ -994,9 +994,6 @@ collect_outer_variable_names(ID id, VALUE val, void *ptr) https://github.com/ruby/ruby/blob/trunk/vm.c#L994 return ID_TABLE_CONTINUE; } -VALUE rb_ractor_make_shareable(VALUE obj); -VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name); - static const rb_env_t * env_copy(const VALUE *src_ep, VALUE read_only_variables) { @@ -3182,6 +3179,12 @@ m_core_make_shareable(VALUE recv, VALUE obj) https://github.com/ruby/ruby/blob/trunk/vm.c#L3179 } static VALUE +m_core_make_shareable_copy(VALUE recv, VALUE obj) +{ + return rb_ractor_make_shareable_copy(obj); +} + +static VALUE m_core_ensure_shareable(VALUE recv, VALUE obj, VALUE name) { return rb_ractor_ensure_shareable(obj, name); @@ -3352,6 +3355,7 @@ Init_VM(void) https://github.com/ruby/ruby/blob/trunk/vm.c#L3355 rb_define_method_id(klass, idProc, f_proc, 0); rb_define_method_id(klass, idLambda, f_lambda, 0); rb_define_method(klass, "make_shareable", m_core_make_shareable, 1); + rb_define_method(klass, "make_shareable_copy", m_core_make_shareable_copy, 1); rb_define_method(klass, "ensure_shareable", m_core_ensure_shareable, 2); rb_obj_freeze(fcore); RBASIC_CLEAR_CLASS(klass); -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/