@@ -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+
14861485static 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
14971496static 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
15951594static 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
16171616static 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
16321631static 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
17211723VALUE
17221724rb_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