@@ -1229,6 +1229,67 @@ rb_obj_set_shareable(VALUE obj)
12291229 return obj ;
12301230}
12311231
1232+ /* Global opt-in that makes Ractor.make_shareable record objects as
1233+ * "warning-shareable" instead of freezing them. Those objects are kept in an
1234+ * identity hash so rb_check_frozen() and selected fast paths can warn if they
1235+ * are mutated. */
1236+ bool ruby_ractor_warn_frozen_error = false;
1237+ bool ruby_ractor_warn_frozen_error_objects_enabled = false;
1238+ static VALUE ractor_warn_frozen_error_objects = Qnil ;
1239+
1240+ static VALUE
1241+ ractor_warn_frozen_error_objects_hash (void )
1242+ {
1243+ if (NIL_P (ractor_warn_frozen_error_objects )) {
1244+ ractor_warn_frozen_error_objects = rb_ident_hash_new ();
1245+ rb_obj_hide (ractor_warn_frozen_error_objects );
1246+ rb_gc_register_mark_object (ractor_warn_frozen_error_objects );
1247+ }
1248+ return ractor_warn_frozen_error_objects ;
1249+ }
1250+
1251+ bool
1252+ rb_ractor_warn_frozen_error_marked_p (VALUE obj )
1253+ {
1254+ if (RB_SPECIAL_CONST_P (obj ) || !ruby_ractor_warn_frozen_error_objects_enabled ) {
1255+ return false;
1256+ }
1257+
1258+ if (NIL_P (ractor_warn_frozen_error_objects )) {
1259+ return false;
1260+ }
1261+
1262+ return RTEST (rb_hash_lookup2 (ractor_warn_frozen_error_objects , obj , Qfalse ));
1263+ }
1264+
1265+ bool
1266+ rb_ractor_warn_frozen_error_warn (VALUE obj )
1267+ {
1268+ if (!rb_ractor_warn_frozen_error_marked_p (obj )) {
1269+ return false;
1270+ }
1271+
1272+ rb_category_warn (RB_WARN_CATEGORY_RACTOR_ISOLATION ,
1273+ "would raise FrozenError: can't modify object passed to Ractor.make_shareable with Ractor.warn_frozen_error=true: %" PRIsVALUE ,
1274+ rb_obj_class (obj ));
1275+ return true;
1276+ }
1277+
1278+ static void
1279+ ractor_warn_frozen_error_mark (VALUE obj )
1280+ {
1281+ if (RB_SPECIAL_CONST_P (obj )) {
1282+ return ;
1283+ }
1284+
1285+ if (RB_TYPE_P (obj , T_STRING )) {
1286+ rb_str_make_independent (obj );
1287+ }
1288+
1289+ rb_hash_aset (ractor_warn_frozen_error_objects_hash (), obj , Qtrue );
1290+ ruby_ractor_warn_frozen_error_objects_enabled = true;
1291+ }
1292+
12321293/// traverse function
12331294
12341295// 2: stop search
@@ -1639,6 +1700,64 @@ make_shareable_check_shareable(VALUE obj, struct obj_traverse_data *data)
16391700 return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
16401701}
16411702
1703+ static enum obj_traverse_iterator_result
1704+ make_shareable_warn_check_shareable (VALUE obj , struct obj_traverse_data * data )
1705+ {
1706+ VM_ASSERT (!SPECIAL_CONST_P (obj ));
1707+
1708+ if (rb_ractor_shareable_p (obj )) {
1709+ return traverse_skip ;
1710+ }
1711+ else if (!allow_frozen_shareable_p (obj )) {
1712+ VM_ASSERT (RB_TYPE_P (obj , T_DATA ));
1713+ const rb_data_type_t * type = RTYPEDDATA_TYPE (obj );
1714+
1715+ if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
1716+ if (obj_refer_only_shareables_p (obj )) {
1717+ ractor_warn_frozen_error_mark (obj );
1718+ return traverse_skip ;
1719+ }
1720+ else {
1721+ rb_raise (rb_eRactorError ,
1722+ "can not make shareable object for %+" PRIsVALUE " because it refers unshareable objects" , obj );
1723+ }
1724+ }
1725+ else if (rb_obj_is_proc (obj )) {
1726+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1727+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1728+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1729+
1730+ if (data -> exception ) {
1731+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1732+ }
1733+ return traverse_stop ;
1734+ }
1735+ return traverse_cont ;
1736+ }
1737+ else {
1738+ return traverse_stop ;
1739+ }
1740+ }
1741+
1742+ switch (TYPE (obj )) {
1743+ case T_IMEMO :
1744+ return traverse_skip ;
1745+ case T_OBJECT :
1746+ break ;
1747+ default :
1748+ break ;
1749+ }
1750+
1751+ return traverse_cont ;
1752+ }
1753+
1754+ static enum obj_traverse_iterator_result
1755+ mark_warn_shareable (VALUE obj )
1756+ {
1757+ ractor_warn_frozen_error_mark (obj );
1758+ return traverse_cont ;
1759+ }
1760+
16421761static enum obj_traverse_iterator_result
16431762mark_shareable (VALUE obj )
16441763{
@@ -1655,7 +1774,12 @@ rb_ractor_make_shareable(VALUE obj)
16551774{
16561775 VALUE chain = Qnil ;
16571776 VALUE exception = Qfalse ;
1658- if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1777+ rb_obj_traverse_enter_func enter_func = ruby_ractor_warn_frozen_error ?
1778+ make_shareable_warn_check_shareable : make_shareable_check_shareable ;
1779+ rb_obj_traverse_final_func final_func = ruby_ractor_warn_frozen_error ?
1780+ mark_warn_shareable : mark_shareable ;
1781+
1782+ if (rb_obj_traverse (obj , enter_func , null_leave , final_func , & chain , & exception )) {
16591783 if (!exception ) {
16601784 exception = rb_exc_new3 (rb_eRactorError , rb_sprintf ("can not make shareable object for %+" PRIsVALUE , obj ));
16611785 }
@@ -2235,7 +2359,11 @@ ractor_obj_clone(VALUE obj)
22352359static enum obj_traverse_iterator_result
22362360copy_enter (VALUE obj , struct obj_traverse_replace_data * data )
22372361{
2238- if (rb_ractor_shareable_p (obj )) {
2362+ if (rb_ractor_warn_frozen_error_marked_p (obj )) {
2363+ data -> replacement = ractor_obj_clone (obj );
2364+ return traverse_cont ;
2365+ }
2366+ else if (rb_ractor_shareable_p (obj )) {
22392367 data -> replacement = obj ;
22402368 return traverse_skip ;
22412369 }
@@ -2914,4 +3042,17 @@ ractor_check_isolation_p(rb_execution_context_t *ec, VALUE self)
29143042 return rb_thread_ractor_isolation_check_p () ? Qtrue : Qfalse ;
29153043}
29163044
3045+ static VALUE
3046+ ractor_warn_frozen_error (rb_execution_context_t * ec , VALUE self )
3047+ {
3048+ return ruby_ractor_warn_frozen_error ? Qtrue : Qfalse ;
3049+ }
3050+
3051+ static VALUE
3052+ ractor_warn_frozen_error_set (rb_execution_context_t * ec , VALUE self , VALUE enabled )
3053+ {
3054+ ruby_ractor_warn_frozen_error = RTEST (enabled );
3055+ return enabled ;
3056+ }
3057+
29173058#include "ractor.rbinc"
0 commit comments