mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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.
This commit is contained in:
parent
1e215a66d2
commit
6f29716f9f
Notes:
git
2020-12-24 14:29:13 +09:00
6 changed files with 73 additions and 15 deletions
|
@ -125,6 +125,7 @@ The directive can specify special treatment for values assigned to constants:
|
||||||
* +none+: (default)
|
* +none+: (default)
|
||||||
* +literal+: literals are implicitly frozen, others must be Ractor-shareable
|
* +literal+: literals are implicitly frozen, others must be Ractor-shareable
|
||||||
* +experimental_everything+: all made shareable
|
* +experimental_everything+: all made shareable
|
||||||
|
* +experimental_copy+: copy deeply and make it shareable
|
||||||
|
|
||||||
==== Mode +none+ (default)
|
==== Mode +none+ (default)
|
||||||
|
|
||||||
|
@ -168,22 +169,46 @@ The method Module#const_set is not affected.
|
||||||
In this mode, all values assigned to constants are made shareable.
|
In this mode, all values assigned to constants are made shareable.
|
||||||
|
|
||||||
# shareable_constant_value: experimental_everything
|
# shareable_constant_value: experimental_everything
|
||||||
FOO = Set.new[1, 2, {foo: []}] # => ok, since this is
|
FOO = Set[1, 2, {foo: []}]
|
||||||
# same as `Set.new[1, 2, {foo: [].freeze}.freeze].freeze`
|
# same as FOO = Ractor.make_sharable(...)
|
||||||
|
# OR same as `FOO = Set[1, 2, {foo: [].freeze}.freeze].freeze`
|
||||||
|
|
||||||
var = [{foo: []}]
|
var = [{foo: []}]
|
||||||
var.frozen? # => false (assignment was made to local variable)
|
var.frozen? # => false (assignment was made to local variable)
|
||||||
X = var # => calls `Ractor.make_shareable(var)`
|
X = var # => calls `Ractor.make_shareable(var)`
|
||||||
var.frozen? # => true
|
var.frozen? # => true
|
||||||
|
|
||||||
This mode is "experimental", because it might be too error prone,
|
This mode is "experimental", because it might be error prone, for
|
||||||
for example by deep-freezing the constants of an external resource
|
example by deep-freezing the constants of an external resource which
|
||||||
which could cause errors:
|
could cause errors:
|
||||||
|
|
||||||
# shareable_constant_value: experimental_everything
|
# shareable_constant_value: experimental_everything
|
||||||
FOO = SomeGem::Something::FOO
|
FOO = SomeGem::Something::FOO
|
||||||
# => deep freezes the gem's constant!
|
# => 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.
|
The method Module#const_set is not affected.
|
||||||
|
|
||||||
==== Scope
|
==== Scope
|
||||||
|
|
|
@ -61,4 +61,7 @@ rb_ractor_shareable_p(VALUE obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE rb_ractor_make_shareable(VALUE obj);
|
||||||
|
VALUE rb_ractor_make_shareable_copy(VALUE obj);
|
||||||
|
|
||||||
#endif /* RUBY_RACTOR_H */
|
#endif /* RUBY_RACTOR_H */
|
||||||
|
|
21
parse.y
21
parse.y
|
@ -58,6 +58,7 @@ struct lex_context;
|
||||||
enum shareability {
|
enum shareability {
|
||||||
shareable_none,
|
shareable_none,
|
||||||
shareable_literal,
|
shareable_literal,
|
||||||
|
shareable_copy,
|
||||||
shareable_everything,
|
shareable_everything,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8053,6 +8054,10 @@ parser_set_shareable_constant_value(struct parser_params *p, const char *name, c
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'e': case 'E':
|
case 'e': case 'E':
|
||||||
|
if (STRCASECMP(val, "experimental_copy") == 0) {
|
||||||
|
p->ctxt.shareable_constant_value = shareable_copy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (STRCASECMP(val, "experimental_everything") == 0) {
|
if (STRCASECMP(val, "experimental_everything") == 0) {
|
||||||
p->ctxt.shareable_constant_value = shareable_everything;
|
p->ctxt.shareable_constant_value = shareable_everything;
|
||||||
return;
|
return;
|
||||||
|
@ -11048,12 +11053,19 @@ const_decl_path(struct parser_params *p, NODE **dest)
|
||||||
extern VALUE rb_mRubyVMFrozenCore;
|
extern VALUE rb_mRubyVMFrozenCore;
|
||||||
|
|
||||||
static NODE *
|
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);
|
NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, 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"),
|
return NEW_CALL(fcore, rb_intern("make_shareable"),
|
||||||
NEW_LIST(value, loc), loc);
|
NEW_LIST(value, loc), loc);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static NODE *
|
static NODE *
|
||||||
ensure_shareable_node(struct parser_params *p, NODE **dest, NODE *value, const YYLTYPE *loc)
|
ensure_shareable_node(struct parser_params *p, NODE **dest, NODE *value, const YYLTYPE *loc)
|
||||||
|
@ -11089,8 +11101,6 @@ shareable_literal_value(NODE *node)
|
||||||
#define SHAREABLE_BARE_EXPRESSION 1
|
#define SHAREABLE_BARE_EXPRESSION 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VALUE rb_ractor_make_shareable(VALUE obj);
|
|
||||||
|
|
||||||
static NODE *
|
static NODE *
|
||||||
shareable_literal_constant(struct parser_params *p, enum shareability shareable,
|
shareable_literal_constant(struct parser_params *p, enum shareability shareable,
|
||||||
NODE **dest, NODE *value, const YYLTYPE *loc, size_t level)
|
NODE **dest, NODE *value, const YYLTYPE *loc, size_t level)
|
||||||
|
@ -11207,7 +11217,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
|
||||||
if (NIL_P(lit)) {
|
if (NIL_P(lit)) {
|
||||||
// if shareable_literal, all elements should have been ensured
|
// if shareable_literal, all elements should have been ensured
|
||||||
// as shareable
|
// as shareable
|
||||||
value = make_shareable_node(p, value, loc);
|
value = make_shareable_node(p, value, false, loc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nd_set_type(value, NODE_LIT);
|
nd_set_type(value, NODE_LIT);
|
||||||
|
@ -11235,11 +11245,12 @@ shareable_constant_value(struct parser_params *p, enum shareability shareable,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case shareable_copy:
|
||||||
case shareable_everything:
|
case shareable_everything:
|
||||||
{
|
{
|
||||||
NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
|
NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
|
||||||
if (lit) return lit;
|
if (lit) return lit;
|
||||||
return make_shareable_node(p, value, loc);
|
return make_shareable_node(p, value, shareable == shareable_copy, loc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,8 @@ bool rb_ractor_main_p_(void);
|
||||||
void rb_ractor_finish_marking(void);
|
void rb_ractor_finish_marking(void);
|
||||||
void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th);
|
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
|
RUBY_SYMBOL_EXPORT_BEGIN
|
||||||
bool rb_ractor_shareable_p_continue(VALUE obj);
|
bool rb_ractor_shareable_p_continue(VALUE obj);
|
||||||
|
|
||||||
|
|
|
@ -1205,6 +1205,7 @@ x = __ENCODING__
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_shareable_constant_value_simple
|
def test_shareable_constant_value_simple
|
||||||
|
obj = [['unsharable_value']]
|
||||||
a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}")
|
a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}")
|
||||||
begin;
|
begin;
|
||||||
# shareable_constant_value: experimental_everything
|
# shareable_constant_value: experimental_everything
|
||||||
|
@ -1222,6 +1223,18 @@ x = __ENCODING__
|
||||||
assert_ractor_shareable(c)
|
assert_ractor_shareable(c)
|
||||||
assert_equal([1], a[0])
|
assert_equal([1], a[0])
|
||||||
assert_ractor_shareable(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
|
end
|
||||||
|
|
||||||
def test_shareable_constant_value_nested
|
def test_shareable_constant_value_nested
|
||||||
|
|
10
vm.c
10
vm.c
|
@ -994,9 +994,6 @@ collect_outer_variable_names(ID id, VALUE val, void *ptr)
|
||||||
return ID_TABLE_CONTINUE;
|
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 *
|
static const rb_env_t *
|
||||||
env_copy(const VALUE *src_ep, VALUE read_only_variables)
|
env_copy(const VALUE *src_ep, VALUE read_only_variables)
|
||||||
{
|
{
|
||||||
|
@ -3181,6 +3178,12 @@ m_core_make_shareable(VALUE recv, VALUE obj)
|
||||||
return rb_ractor_make_shareable(obj);
|
return rb_ractor_make_shareable(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
m_core_make_shareable_copy(VALUE recv, VALUE obj)
|
||||||
|
{
|
||||||
|
return rb_ractor_make_shareable_copy(obj);
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
m_core_ensure_shareable(VALUE recv, VALUE obj, VALUE name)
|
m_core_ensure_shareable(VALUE recv, VALUE obj, VALUE name)
|
||||||
{
|
{
|
||||||
|
@ -3352,6 +3355,7 @@ Init_VM(void)
|
||||||
rb_define_method_id(klass, idProc, f_proc, 0);
|
rb_define_method_id(klass, idProc, f_proc, 0);
|
||||||
rb_define_method_id(klass, idLambda, f_lambda, 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", 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_define_method(klass, "ensure_shareable", m_core_ensure_shareable, 2);
|
||||||
rb_obj_freeze(fcore);
|
rb_obj_freeze(fcore);
|
||||||
RBASIC_CLEAR_CLASS(klass);
|
RBASIC_CLEAR_CLASS(klass);
|
||||||
|
|
Loading…
Reference in a new issue