Skip to content

Commit adf5510

Browse files
Simplify RUBY_RACTOR_ISOLATION code for readability
Make strict and warning-only Proc checks explicit, simplify warning control flow, and consolidate method-call isolation checks. Remove an unnecessary Proc-copy wrapper while preserving behavior.
1 parent 4b54196 commit adf5510

3 files changed

Lines changed: 86 additions & 81 deletions

File tree

‎ractor.c‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4293,7 +4293,9 @@ static const struct st_hash_type isolation_warn_hash_type = {
42934293
static bool
42944294
isolation_warnings_enabled_p(void)
42954295
{
4296-
return !GET_EC()->ractor_isolation_warning && !NIL_P(ruby_verbose) &&
4296+
if (GET_EC()->ractor_isolation_warning) return false;
4297+
4298+
return !NIL_P(ruby_verbose) &&
42974299
rb_warning_category_enabled_p(RB_WARN_CATEGORY_RACTOR_ISOLATION);
42984300
}
42994301

@@ -4302,24 +4304,24 @@ static VALUE
43024304
isolation_source_location(int *line)
43034305
{
43044306
const rb_execution_context_t *ec = GET_EC();
4305-
const rb_control_frame_t *cfp = ec->cfp;
4306-
4307-
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
4308-
if (VM_FRAME_RUBYFRAME_P(cfp) && CFP_ISEQ(cfp)) {
4309-
VALUE path = rb_iseq_path(CFP_ISEQ(cfp));
4310-
if (strncmp("<internal:", RSTRING_PTR(path), 10) != 0) {
4311-
*line = rb_vm_get_sourceline(cfp);
4312-
return path;
4313-
}
4314-
}
4315-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
4307+
4308+
for (const rb_control_frame_t *cfp = ec->cfp;
4309+
!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp);
4310+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
4311+
if (!VM_FRAME_RUBYFRAME_P(cfp) || !CFP_ISEQ(cfp)) continue;
4312+
4313+
VALUE path = rb_iseq_path(CFP_ISEQ(cfp));
4314+
if (strncmp("<internal:", RSTRING_PTR(path), 10) == 0) continue;
4315+
4316+
*line = rb_vm_get_sourceline(cfp);
4317+
return path;
43164318
}
43174319
*line = 0;
43184320
return Qnil;
43194321
}
43204322

43214323
static bool
4322-
isolation_warn_first_p(VALUE message, VALUE path, int line)
4324+
isolation_warn_first_occurrence_p(VALUE message, VALUE path, int line)
43234325
{
43244326
bool first = true;
43254327
RB_VM_LOCKING() {
@@ -4334,6 +4336,7 @@ isolation_warn_first_p(VALUE message, VALUE path, int line)
43344336
isolation_warn_suppressed++;
43354337
}
43364338
else {
4339+
// Own copies of both strings outside Ruby's heap for this VM-wide table.
43374340
size_t file_size = strlen(lookup.file) + 1;
43384341
size_t message_size = strlen(lookup.message) + 1;
43394342
struct isolation_warn_key *key = malloc(sizeof(*key) + file_size + message_size);
@@ -4345,6 +4348,7 @@ isolation_warn_first_p(VALUE message, VALUE path, int line)
43454348
*key = (struct isolation_warn_key){file, text, line};
43464349
st_add_direct(isolation_warn_tbl, (st_data_t)key, 0);
43474350
}
4351+
// If allocation fails, still report the warning.
43484352
}
43494353
}
43504354
RB_GC_GUARD(message);
@@ -4358,7 +4362,9 @@ isolation_warn_emit(VALUE message)
43584362
int line;
43594363
VALUE path = isolation_source_location(&line);
43604364
StringValueCStr(message);
4361-
if (ruby_ractor_isolation_enabled < 2 && !isolation_warn_first_p(message, path, line)) return Qnil;
4365+
if (ruby_ractor_isolation_enabled < 2 && !isolation_warn_first_occurrence_p(message, path, line)) {
4366+
return Qnil;
4367+
}
43624368

43634369
const char *file = NIL_P(path) ? NULL : RSTRING_PTR(path);
43644370
rb_category_compile_warn(RB_WARN_CATEGORY_RACTOR_ISOLATION, file, line, "%s", RSTRING_PTR(message));
@@ -4368,7 +4374,7 @@ isolation_warn_emit(VALUE message)
43684374
}
43694375

43704376
static VALUE
4371-
isolation_warn_end(VALUE ec_ptr)
4377+
isolation_warn_clear_guard(VALUE ec_ptr)
43724378
{
43734379
((rb_execution_context_t *)ec_ptr)->ractor_isolation_warning = false;
43744380
return Qnil;
@@ -4384,7 +4390,7 @@ rb_ractor_isolation_warn(VALUE message)
43844390
// Keep the guard fiber-local and restore it even if the hook raises.
43854391
rb_execution_context_t *ec = GET_EC();
43864392
ec->ractor_isolation_warning = true;
4387-
rb_ensure(isolation_warn_emit, message, isolation_warn_end, (VALUE)ec);
4393+
rb_ensure(isolation_warn_emit, message, isolation_warn_clear_guard, (VALUE)ec);
43884394
}
43894395

43904396
void

‎vm.c‎

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,21 +1400,15 @@ rb_proc_dup_0(VALUE self)
14001400
return proc_dup_0(self, true);
14011401
}
14021402

1403-
static VALUE
1404-
proc_dup(VALUE self, bool preserve_shareability)
1403+
VALUE
1404+
rb_proc_dup(VALUE self)
14051405
{
1406-
VALUE procval = proc_dup_0(self, preserve_shareability);
1406+
VALUE procval = rb_proc_dup_0(self);
14071407
VALUE recipe = rb_proc_refinements_recipe(self);
14081408
if (!NIL_P(recipe)) rb_proc_set_refinements_recipe(procval, recipe);
14091409
return procval;
14101410
}
14111411

1412-
VALUE
1413-
rb_proc_dup(VALUE self)
1414-
{
1415-
return proc_dup(self, true);
1416-
}
1417-
14181412
/* Proc#refined: build a Proc that runs `iseq` with the refinements of
14191413
* `recipe`, sharing self's environment. `iseq` is normally self's own block
14201414
* iseq, which the copy replaces on the first call. */
@@ -1483,10 +1477,15 @@ collect_outer_variable_names(ID id, VALUE val, void *ptr)
14831477
return ID_TABLE_CONTINUE;
14841478
}
14851479

1480+
enum proc_isolation_mode {
1481+
PROC_ISOLATION_STRICT,
1482+
PROC_ISOLATION_WARN_ONLY,
1483+
};
1484+
14861485
static void
1487-
proc_isolation_violation_str(VALUE message, bool warn)
1486+
proc_isolation_violation_str(VALUE message, enum proc_isolation_mode mode)
14881487
{
1489-
if (warn) {
1488+
if (mode == PROC_ISOLATION_WARN_ONLY) {
14901489
rb_ractor_isolation_warn(message);
14911490
}
14921491
else {
@@ -1495,7 +1494,7 @@ proc_isolation_violation_str(VALUE message, bool warn)
14951494
}
14961495

14971496
static const rb_env_t *
1498-
env_copy(const VALUE *src_ep, VALUE read_only_variables, bool warn, bool *valid)
1497+
env_copy(const VALUE *src_ep, VALUE read_only_variables, enum proc_isolation_mode mode, bool *can_isolate)
14991498
{
15001499
const rb_env_t *src_env = (rb_env_t *)VM_ENV_ENVVAL(src_ep);
15011500
VM_ASSERT(src_env->ep == src_ep);
@@ -1535,16 +1534,16 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables, bool warn, bool *valid)
15351534
VALUE name = rb_id2str(id);
15361535
VALUE msg = rb_sprintf("cannot make a shareable Proc because "
15371536
"the outer variable '%" PRIsVALUE "' may be reassigned.", name);
1538-
proc_isolation_violation_str(msg, warn);
1539-
*valid = false;
1537+
proc_isolation_violation_str(msg, mode);
1538+
*can_isolate = false;
15401539
}
15411540

15421541
// check shareable
15431542
VALUE v = src_env->env[j];
15441543
if (!rb_ractor_shareable_p(v)) {
15451544
VALUE name = rb_id2str(id);
15461545
VALUE msg;
1547-
if (warn) {
1546+
if (mode == PROC_ISOLATION_WARN_ONLY) {
15481547
msg = rb_sprintf("cannot make a shareable Proc because it can refer "
15491548
"an unshareable object of class %+" PRIsVALUE " from ",
15501549
rb_class_of(v));
@@ -1557,8 +1556,8 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables, bool warn, bool *valid)
15571556
rb_str_catf(msg, "variable '%" PRIsVALUE "'", name);
15581557
else
15591558
rb_str_cat_cstr(msg, "a hidden variable");
1560-
proc_isolation_violation_str(msg, warn);
1561-
*valid = false;
1559+
proc_isolation_violation_str(msg, mode);
1560+
*can_isolate = false;
15621561
}
15631562
RB_OBJ_WRITE((VALUE)copied_env, &env_body[j], v);
15641563
rb_ary_delete_at(read_only_variables, i);
@@ -1570,7 +1569,7 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables, bool warn, bool *valid)
15701569

15711570
if (!VM_ENV_LOCAL_P(src_ep)) {
15721571
const VALUE *prev_ep = VM_ENV_PREV_EP(src_env->ep);
1573-
const rb_env_t *new_prev_env = env_copy(prev_ep, read_only_variables, warn, valid);
1572+
const rb_env_t *new_prev_env = env_copy(prev_ep, read_only_variables, mode, can_isolate);
15741573
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_GUARDED_PREV_EP(new_prev_env->ep);
15751574
RB_OBJ_WRITTEN(copied_env, Qundef, new_prev_env);
15761575
VM_ENV_FLAGS_UNSET(ep, VM_ENV_FLAG_LOCAL);
@@ -1586,18 +1585,18 @@ env_copy(const VALUE *src_ep, VALUE read_only_variables, bool warn, bool *valid)
15861585
ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE;
15871586
}
15881587

1589-
if (*valid) {
1588+
if (*can_isolate) {
15901589
RB_OBJ_SET_SHAREABLE((VALUE)copied_env);
15911590
}
15921591
return copied_env;
15931592
}
15941593

15951594
static bool
1596-
proc_isolate_env(VALUE self, rb_proc_t *proc, VALUE read_only_variables, bool warn, bool valid)
1595+
proc_isolate_env(VALUE self, rb_proc_t *proc, VALUE read_only_variables, enum proc_isolation_mode mode, bool can_isolate)
15971596
{
15981597
const struct rb_captured_block *captured = &proc->block.as.captured;
1599-
const rb_env_t *env = env_copy(captured->ep, read_only_variables, warn, &valid);
1600-
if (!valid) return false;
1598+
const rb_env_t *env = env_copy(captured->ep, read_only_variables, mode, &can_isolate);
1599+
if (!can_isolate) return false;
16011600

16021601
*((const VALUE **)&proc->block.as.captured.ep) = env->ep;
16031602
RB_OBJ_WRITTEN(self, Qundef, env);
@@ -1615,22 +1614,23 @@ proc_has_ivar_i(ID name, VALUE val, st_data_t arg)
16151614
}
16161615

16171616
static void
1618-
proc_check_isolation_ivars(VALUE self, bool warn)
1617+
proc_check_isolation_ivars(VALUE self, enum proc_isolation_mode mode)
16191618
{
1619+
if (LIKELY(!rb_obj_shape_has_ivars(self))) return;
1620+
16201621
/* ivars are not traversed here, so their values may be unshareable */
1621-
if (UNLIKELY(rb_obj_shape_has_ivars(self))) {
1622-
bool has_ivar = false;
1623-
rb_ivar_foreach(self, proc_has_ivar_i, (st_data_t)&has_ivar);
1622+
bool has_ivar = false;
1623+
rb_ivar_foreach(self, proc_has_ivar_i, (st_data_t)&has_ivar);
16241624

1625-
if (has_ivar) {
1626-
proc_isolation_violation_str(
1627-
rb_str_new_cstr("can not isolate a Proc because it has instance variables"), warn);
1628-
}
1625+
if (has_ivar) {
1626+
proc_isolation_violation_str(
1627+
rb_str_new_cstr("can not isolate a Proc because it has instance variables"), mode);
16291628
}
16301629
}
16311630

16321631
static VALUE
1633-
proc_shared_outer_variables(struct rb_id_table *outer_variables, bool isolate, const char *message, bool warn, bool *valid)
1632+
proc_shared_outer_variables(struct rb_id_table *outer_variables, bool isolate, const char *message,
1633+
enum proc_isolation_mode mode, bool *can_isolate)
16341634
{
16351635
struct collect_outer_variable_name_data data = {
16361636
.isolate = isolate,
@@ -1653,13 +1653,13 @@ proc_shared_outer_variables(struct rb_id_table *outer_variables, bool isolate, c
16531653
}
16541654
if (*sep == ',') rb_str_cat_cstr(str, ")");
16551655
rb_str_cat_cstr(str, data.yield ? " and uses 'yield'." : ".");
1656-
proc_isolation_violation_str(str, warn);
1657-
if (valid) *valid = false;
1656+
proc_isolation_violation_str(str, mode);
1657+
if (can_isolate) *can_isolate = false;
16581658
}
16591659
else if (data.yield) {
16601660
VALUE str = rb_sprintf("can not %s because it uses 'yield'.", message);
1661-
proc_isolation_violation_str(str, warn);
1662-
if (valid) *valid = false;
1661+
proc_isolation_violation_str(str, mode);
1662+
if (can_isolate) *can_isolate = false;
16631663
}
16641664

16651665
return data.read_only;
@@ -1681,15 +1681,16 @@ rb_proc_isolate_bang(VALUE self, VALUE replace_self)
16811681
}
16821682

16831683
if (ISEQ_BODY(iseq)->outer_variables) {
1684-
proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, true, "isolate a Proc", false, NULL);
1684+
proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, true, "isolate a Proc",
1685+
PROC_ISOLATION_STRICT, NULL);
16851686
}
16861687

1687-
if (!proc_isolate_env(self, proc, Qfalse, false, true)) return self;
1688+
if (!proc_isolate_env(self, proc, Qfalse, PROC_ISOLATION_STRICT, true)) return self;
16881689
proc->header.is_isolated = TRUE;
16891690
RB_OBJ_WRITE(self, &proc->block.as.captured.self, Qnil);
16901691
}
16911692

1692-
proc_check_isolation_ivars(self, false);
1693+
proc_check_isolation_ivars(self, PROC_ISOLATION_STRICT);
16931694

16941695
RB_OBJ_SET_SHAREABLE(self);
16951696
return self;
@@ -1712,46 +1713,48 @@ rb_proc_check_isolation_warn(VALUE self)
17121713
if (iseq) {
17131714
rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self);
17141715
if (proc->block.type == block_type_iseq && ISEQ_BODY(iseq)->outer_variables) {
1715-
proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, true, "isolate a Proc", true, NULL);
1716+
proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, true, "isolate a Proc",
1717+
PROC_ISOLATION_WARN_ONLY, NULL);
17161718
}
17171719
}
1718-
proc_check_isolation_ivars(self, true);
1720+
proc_check_isolation_ivars(self, PROC_ISOLATION_WARN_ONLY);
17191721
}
17201722

17211723
VALUE
17221724
rb_proc_ractor_make_shareable(VALUE self, VALUE replace_self)
17231725
{
17241726
const rb_iseq_t *iseq = vm_proc_iseq(self);
1725-
bool warn = rb_ractor_isolation_check_p();
1727+
enum proc_isolation_mode mode = rb_ractor_isolation_check_p() ?
1728+
PROC_ISOLATION_WARN_ONLY : PROC_ISOLATION_STRICT;
17261729

17271730
if (iseq) {
17281731
rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self);
17291732

17301733
if (proc->block.type != block_type_iseq) rb_raise(rb_eRuntimeError, "not supported yet");
17311734

1732-
bool valid = true;
1735+
bool can_isolate = true;
17331736
VALUE proc_self = UNDEF_P(replace_self) ? vm_block_self(&proc->block) : replace_self;
17341737
if (!rb_ractor_shareable_p(proc_self)) {
17351738
VALUE message = rb_sprintf("Proc's self is not shareable: %" PRIsVALUE, self);
1736-
proc_isolation_violation_str(message, warn);
1737-
valid = false;
1739+
proc_isolation_violation_str(message, mode);
1740+
can_isolate = false;
17381741
}
17391742

17401743
VALUE read_only_variables = Qfalse;
17411744

17421745
if (ISEQ_BODY(iseq)->outer_variables) {
17431746
read_only_variables =
17441747
proc_shared_outer_variables(ISEQ_BODY(iseq)->outer_variables, false,
1745-
"make a Proc shareable", warn, &valid);
1748+
"make a Proc shareable", mode, &can_isolate);
17461749
}
17471750

1748-
valid = proc_isolate_env(self, proc, read_only_variables, warn, valid);
1751+
can_isolate = proc_isolate_env(self, proc, read_only_variables, mode, can_isolate);
17491752
if (!UNDEF_P(replace_self)) {
17501753
RB_OBJ_WRITE(self, &proc->block.as.captured.self, replace_self);
17511754
}
17521755
// Diagnostic fallback keeps the original closure and requested receiver,
17531756
// but must not mark an invalid Proc isolated or shareable.
1754-
if (!valid) return self;
1757+
if (!can_isolate) return self;
17551758
proc->header.is_isolated = TRUE;
17561759
}
17571760
else {
@@ -1761,8 +1764,8 @@ rb_proc_ractor_make_shareable(VALUE self, VALUE replace_self)
17611764
VALUE proc_self = vm_block_self(block);
17621765
if (!rb_ractor_shareable_p(proc_self)) {
17631766
VALUE message = rb_sprintf("Proc's self is not shareable: %" PRIsVALUE, self);
1764-
proc_isolation_violation_str(message, warn);
1765-
if (warn) return self;
1767+
proc_isolation_violation_str(message, mode);
1768+
return self;
17661769
}
17671770
}
17681771

@@ -1775,7 +1778,9 @@ rb_proc_ractor_make_shareable_copy(VALUE self, VALUE replace_self)
17751778
{
17761779
// Rebinding self can make a shareable Proc unshareable in diagnostic mode.
17771780
// The copy must only become shareable after its new receiver is validated.
1778-
VALUE copy = proc_dup(self, false);
1781+
VALUE copy = proc_dup_0(self, false);
1782+
VALUE recipe = rb_proc_refinements_recipe(self);
1783+
if (!NIL_P(recipe)) rb_proc_set_refinements_recipe(copy, recipe);
17791784
return rb_proc_ractor_make_shareable(copy, replace_self);
17801785
}
17811786

0 commit comments

Comments
 (0)