summaryrefslogtreecommitdiff
path: root/vm.c
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2020-10-30 00:32:53 +0900
committerKoichi Sasada <ko1@atdot.net>2020-10-30 03:12:09 +0900
commit5d97bdc2dcb835c877010daa033cc2b1dfeb86d6 (patch)
tree147bbd2e687a9db2bbd5fede670690eae644ebe1 /vm.c
parent502d6d845946f367ffb6e972e9c4ac401da94e99 (diff)
Ractor.make_shareable(a_proc)
Ractor.make_shareable() supports Proc object if (1) a Proc only read outer local variables (no assignments) (2) read outer local variables are shareable. Read local variables are stored in a snapshot, so after making shareable Proc, any assignments are not affeect like that: ```ruby a = 1 pr = Ractor.make_shareable(Proc.new{p a}) pr.call #=> 1 a = 2 pr.call #=> 1 # `a = 2` doesn't affect ``` [Feature #17284]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3722
Diffstat (limited to 'vm.c')
-rw-r--r--vm.c100
1 files changed, 90 insertions, 10 deletions
diff --git a/vm.c b/vm.c
index 27e39cf..db03873 100644
--- a/vm.c
+++ b/vm.c
@@ -960,13 +960,16 @@ rb_proc_dup(VALUE self)
GetProcPtr(self, src);
procval = proc_create(rb_cProc, &src->block, src->is_from_method, src->is_lambda);
+ if (RB_OBJ_SHAREABLE_P(self)) FL_SET_RAW(procval, RUBY_FL_SHAREABLE);
RB_GC_GUARD(self); /* for: body = rb_proc_dup(body) */
return procval;
}
struct collect_outer_variable_name_data {
VALUE ary;
+ VALUE read_only;
bool yield;
+ bool isolate;
};
static enum rb_id_table_iterator_result
@@ -978,40 +981,70 @@ collect_outer_variable_names(ID id, VALUE val, void *ptr)
data->yield = true;
}
else {
- if (data->ary == Qfalse) data->ary = rb_ary_new();
- rb_ary_push(data->ary, rb_id2str(id));
+ if (data->isolate ||
+ val == Qtrue /* write */) {
+ if (data->ary == Qfalse) data->ary = rb_ary_new();
+ rb_ary_push(data->ary, rb_id2str(id));
+ }
+ else {
+ if (data->read_only == Qfalse) data->read_only = rb_ary_new();
+ rb_ary_push(data->read_only, rb_id2str(id));
+ }
}
return ID_TABLE_CONTINUE;
}
+VALUE rb_ractor_error_class(void);
+
static const rb_env_t *
-env_copy(const VALUE *src_ep)
+env_copy(const VALUE *src_ep, VALUE read_only_variables)
{
const rb_env_t *src_env = (rb_env_t *)VM_ENV_ENVVAL(src_ep);
+ VM_ASSERT(src_env->ep == src_ep);
+
VALUE *env_body = ZALLOC_N(VALUE, src_env->env_size); // fill with Qfalse
VALUE *ep = &env_body[src_env->env_size - 2];
- VM_ASSERT(src_env->ep == src_ep);
+ if (read_only_variables) {
+ for (int i=0; i<RARRAY_LENINT(read_only_variables); i++) {
+ ID id = SYM2ID(rb_str_intern(RARRAY_AREF(read_only_variables, i)));
+
+ for (unsigned int j=0; j<src_env->iseq->body->local_table_size; j++) {
+ if (id == src_env->iseq->body->local_table[j]) {
+ VALUE v = src_env->env[j];
+ if (!rb_ractor_shareable_p(v)) {
+ rb_raise(rb_ractor_error_class(),
+ "can not make shareable Proc because it can refere unshareable object %"
+ PRIsVALUE" from variable `%s'", rb_inspect(v), rb_id2name(id));
+ }
+ env_body[j] = v;
+ rb_ary_delete_at(read_only_variables, i);
+ break;
+ }
+ }
+ }
+ }
ep[VM_ENV_DATA_INDEX_ME_CREF] = src_ep[VM_ENV_DATA_INDEX_ME_CREF];
ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS] | VM_ENV_FLAG_ISOLATED;
if (!VM_ENV_LOCAL_P(src_ep)) {
const VALUE *prev_ep = VM_ENV_PREV_EP(src_env->ep);
- const rb_env_t *new_prev_env = env_copy(prev_ep);
+ const rb_env_t *new_prev_env = env_copy(prev_ep, read_only_variables);
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_GUARDED_PREV_EP(new_prev_env->ep);
}
else {
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE;
}
+
return vm_env_new(ep, env_body, src_env->env_size, src_env->iseq);
}
static void
-proc_isolate_env(VALUE self, rb_proc_t *proc)
+proc_isolate_env(VALUE self, rb_proc_t *proc, VALUE read_only_variables)
{
const struct rb_captured_block *captured = &proc->block.as.captured;
- const rb_env_t *env = env_copy(captured->ep);
+ const rb_env_t *env = env_copy(captured->ep, read_only_variables);
*((const VALUE **)&proc->block.as.captured.ep) = env->ep;
RB_OBJ_WRITTEN(self, Qundef, env);
}
@@ -1019,7 +1052,6 @@ proc_isolate_env(VALUE self, rb_proc_t *proc)
VALUE
rb_proc_isolate_bang(VALUE self)
{
- // check accesses
const rb_iseq_t *iseq = vm_proc_iseq(self);
if (iseq) {
@@ -1028,10 +1060,10 @@ rb_proc_isolate_bang(VALUE self)
if (iseq->body->outer_variables) {
struct collect_outer_variable_name_data data = {
+ .isolate = true,
.ary = Qfalse,
.yield = false,
};
-
rb_id_table_foreach(iseq->body->outer_variables, collect_outer_variable_names, (void *)&data);
if (data.ary != Qfalse) {
@@ -1051,10 +1083,11 @@ rb_proc_isolate_bang(VALUE self)
}
}
- proc_isolate_env(self, proc);
+ proc_isolate_env(self, proc, Qfalse);
proc->is_isolated = TRUE;
}
+ FL_SET_RAW(self, RUBY_FL_SHAREABLE);
return self;
}
@@ -1066,6 +1099,53 @@ rb_proc_isolate(VALUE self)
return dst;
}
+VALUE
+rb_proc_ractor_make_shareable(VALUE self)
+{
+ const rb_iseq_t *iseq = vm_proc_iseq(self);
+
+ if (iseq) {
+ rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self);
+ if (proc->block.type != block_type_iseq) rb_raise(rb_eRuntimeError, "not supported yet");
+
+ VALUE read_only_variables = Qfalse;
+
+ if (iseq->body->outer_variables) {
+ struct collect_outer_variable_name_data data = {
+ .isolate = false,
+ .ary = Qfalse,
+ .read_only = Qfalse,
+ .yield = false,
+ };
+
+ rb_id_table_foreach(iseq->body->outer_variables, collect_outer_variable_names, (void *)&data);
+
+ if (data.ary != Qfalse) {
+ VALUE str = rb_ary_join(data.ary, rb_str_new2(", "));
+ if (data.yield) {
+ rb_raise(rb_eArgError, "can not make a Proc shareable because it accesses outer variables (%s) and uses `yield'.",
+ StringValueCStr(str));
+ }
+ else {
+ rb_raise(rb_eArgError, "can not make a Proc shareable because it accesses outer variables (%s).",
+ StringValueCStr(str));
+ }
+ }
+ else if (data.yield) {
+ rb_raise(rb_eArgError, "can not make a Proc shareable because it uses `yield'.");
+ }
+
+ read_only_variables = data.read_only;
+ }
+
+ proc_isolate_env(self, proc, read_only_variables);
+ proc->is_isolated = TRUE;
+ }
+
+ FL_SET_RAW(self, RUBY_FL_SHAREABLE);
+ return self;
+}
+
MJIT_FUNC_EXPORTED VALUE
rb_vm_make_proc_lambda(const rb_execution_context_t *ec, const struct rb_captured_block *captured, VALUE klass, int8_t is_lambda)
{