@@ -1211,7 +1211,8 @@ enum obj_traverse_iterator_result {
12111211 traverse_stop ,
12121212};
12131213
1214- typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj );
1214+ struct obj_traverse_data ;
1215+ typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj , struct obj_traverse_data * data );
12151216typedef enum obj_traverse_iterator_result (* rb_obj_traverse_leave_func )(VALUE obj );
12161217typedef enum obj_traverse_iterator_result (* rb_obj_traverse_final_func )(VALUE obj );
12171218
@@ -1222,15 +1223,35 @@ struct obj_traverse_data {
12221223 rb_obj_traverse_leave_func leave_func ;
12231224
12241225 st_table * rec ;
1225- VALUE rec_hash ;
1226+ VALUE rec_hash ; // objects seen during traversal
1227+ VALUE * chain ; // reference chain string built during unwinding (NULL if not needed)
1228+ VALUE * exception ; // exception raised trying to freeze an object
12261229};
12271230
1228-
12291231struct obj_traverse_callback_data {
12301232 bool stop ;
12311233 struct obj_traverse_data * data ;
12321234};
12331235
1236+ static inline void
1237+ chain_append (VALUE * chain_ptr , const char * fmt , ...)
1238+ {
1239+ if (!chain_ptr ) return ;
1240+
1241+ va_list args ;
1242+ va_start (args , fmt );
1243+
1244+ if (NIL_P (* chain_ptr )) {
1245+ * chain_ptr = rb_str_new_cstr ("\n" );
1246+ }
1247+ else {
1248+ rb_str_cat_cstr (* chain_ptr , "\n" );
1249+ }
1250+ rb_str_vcatf (* chain_ptr , fmt , args );
1251+
1252+ va_end (args );
1253+ }
1254+
12341255static int obj_traverse_i (VALUE obj , struct obj_traverse_data * data );
12351256
12361257static int
@@ -1239,11 +1260,13 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
12391260 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12401261
12411262 if (obj_traverse_i (key , d -> data )) {
1263+ chain_append (d -> data -> chain , " from hash key %+" PRIsVALUE , key );
12421264 d -> stop = true;
12431265 return ST_STOP ;
12441266 }
12451267
12461268 if (obj_traverse_i (val , d -> data )) {
1269+ chain_append (d -> data -> chain , " from hash value at key %+" PRIsVALUE , key );
12471270 d -> stop = true;
12481271 return ST_STOP ;
12491272 }
@@ -1277,6 +1300,7 @@ obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
12771300 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12781301
12791302 if (obj_traverse_i (val , d -> data )) {
1303+ chain_append (d -> data -> chain , " from instance variable %" PRIsVALUE , rb_id2str (key ));
12801304 d -> stop = true;
12811305 return ST_STOP ;
12821306 }
@@ -1289,7 +1313,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12891313{
12901314 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12911315
1292- switch (data -> enter_func (obj )) {
1316+ switch (data -> enter_func (obj , data )) {
12931317 case traverse_cont : break ;
12941318 case traverse_skip : return 0 ; // skip children
12951319 case traverse_stop : return 1 ; // stop search
@@ -1306,7 +1330,9 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13061330 .data = data ,
13071331 };
13081332 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1309- if (d .stop ) return 1 ;
1333+ if (d .stop ) {
1334+ return 1 ;
1335+ }
13101336
13111337 switch (BUILTIN_TYPE (obj )) {
13121338 // no child node
@@ -1328,14 +1354,21 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13281354
13291355 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13301356 VALUE e = rb_ary_entry (obj , i );
1331- if (obj_traverse_i (e , data )) return 1 ;
1357+ if (obj_traverse_i (e , data )) {
1358+ chain_append (data -> chain , " from array element at index %d" , i );
1359+ return 1 ;
1360+ }
13321361 }
13331362 }
13341363 break ;
13351364
13361365 case T_HASH :
13371366 {
1338- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1367+ const VALUE ifnone = RHASH_IFNONE (obj );
1368+ if (obj_traverse_i (ifnone , data )) {
1369+ chain_append (data -> chain , " from hash default value" );
1370+ return 1 ;
1371+ }
13391372
13401373 struct obj_traverse_callback_data d = {
13411374 .stop = false,
@@ -1352,7 +1385,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13521385 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13531386
13541387 for (long i = 0 ; i < len ; i ++ ) {
1355- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1388+ if (obj_traverse_i (ptr [i ], data )) {
1389+ VALUE members = rb_struct_members (obj );
1390+ VALUE member_name = rb_array_const_ptr (members )[i ];
1391+ chain_append (data -> chain , " from struct member %+" PRIsVALUE , member_name );
1392+ return 1 ;
1393+ }
13561394 }
13571395 }
13581396 break ;
@@ -1423,15 +1461,21 @@ static int
14231461rb_obj_traverse (VALUE obj ,
14241462 rb_obj_traverse_enter_func enter_func ,
14251463 rb_obj_traverse_leave_func leave_func ,
1426- rb_obj_traverse_final_func final_func )
1464+ rb_obj_traverse_final_func final_func ,
1465+ VALUE * chain ,
1466+ VALUE * exception )
14271467{
14281468 struct obj_traverse_data data = {
14291469 .enter_func = enter_func ,
14301470 .leave_func = leave_func ,
14311471 .rec = NULL ,
1472+ .chain = chain ,
1473+ .exception = exception ,
14321474 };
14331475
1434- if (obj_traverse_i (obj , & data )) return 1 ;
1476+ if (obj_traverse_i (obj , & data )) {
1477+ return 1 ;
1478+ }
14351479 if (final_func && data .rec ) {
14361480 struct rb_obj_traverse_final_data f = {final_func , 0 };
14371481 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1456,14 +1500,45 @@ allow_frozen_shareable_p(VALUE obj)
14561500 return false;
14571501}
14581502
1503+ static VALUE
1504+ try_freeze (VALUE obj )
1505+ {
1506+ rb_funcall (obj , idFreeze , 0 );
1507+ return Qtrue ;
1508+ }
1509+
1510+ struct rescue_freeze_data {
1511+ VALUE exception ;
1512+ };
1513+
1514+ static VALUE
1515+ rescue_freeze (VALUE data , VALUE freeze_exception )
1516+ {
1517+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1518+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1519+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1520+ rescue_freeze_data -> exception = exception ;
1521+ return Qfalse ;
1522+ }
1523+
14591524static enum obj_traverse_iterator_result
1460- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1525+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14611526{
14621527 if (!RB_OBJ_FROZEN_RAW (obj )) {
1463- rb_funcall (obj , idFreeze , 0 );
1528+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1529+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1530+ if (data -> exception ) {
1531+ * data -> exception = rescue_freeze_data .exception ;
1532+ }
1533+ return traverse_stop ;
1534+ }
14641535
14651536 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1466- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1537+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1538+ if (data -> exception ) {
1539+ * data -> exception = exception ;
1540+ }
1541+ return traverse_stop ;
14671542 }
14681543
14691544 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1477,7 +1552,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14771552static int obj_refer_only_shareables_p (VALUE obj );
14781553
14791554static enum obj_traverse_iterator_result
1480- make_shareable_check_shareable (VALUE obj )
1555+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14811556{
14821557 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14831558
@@ -1490,7 +1565,8 @@ make_shareable_check_shareable(VALUE obj)
14901565
14911566 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14921567 if (obj_refer_only_shareables_p (obj )) {
1493- make_shareable_check_shareable_freeze (obj , traverse_skip );
1568+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1569+ if (result == traverse_stop ) return traverse_stop ;
14941570 RB_OBJ_SET_SHAREABLE (obj );
14951571 return traverse_skip ;
14961572 }
@@ -1500,11 +1576,19 @@ make_shareable_check_shareable(VALUE obj)
15001576 }
15011577 }
15021578 else if (rb_obj_is_proc (obj )) {
1503- rb_proc_ractor_make_shareable (obj , Qundef );
1579+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1580+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1581+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1582+
1583+ if (data -> exception ) {
1584+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1585+ }
1586+ return traverse_stop ;
1587+ }
15041588 return traverse_cont ;
15051589 }
15061590 else {
1507- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1591+ return traverse_stop ;
15081592 }
15091593 }
15101594
@@ -1529,7 +1613,7 @@ make_shareable_check_shareable(VALUE obj)
15291613 break ;
15301614 }
15311615
1532- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1616+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15331617}
15341618
15351619static enum obj_traverse_iterator_result
@@ -1546,9 +1630,20 @@ mark_shareable(VALUE obj)
15461630VALUE
15471631rb_ractor_make_shareable (VALUE obj )
15481632{
1549- rb_obj_traverse (obj ,
1550- make_shareable_check_shareable ,
1551- null_leave , mark_shareable );
1633+ VALUE chain = Qnil ;
1634+ VALUE exception = Qfalse ;
1635+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1636+ if (exception ) {
1637+ VALUE id_mesg = rb_intern ("mesg" );
1638+ VALUE message = rb_attr_get (exception , id_mesg );
1639+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1640+ rb_ivar_set (exception , id_mesg , message );
1641+ rb_exc_raise (exception );
1642+ }
1643+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1644+ }
1645+ RB_GC_GUARD (chain );
1646+ RB_GC_GUARD (exception );
15521647 return obj ;
15531648}
15541649
@@ -1579,7 +1674,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15791674}
15801675
15811676static enum obj_traverse_iterator_result
1582- shareable_p_enter (VALUE obj )
1677+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15831678{
15841679 if (RB_OBJ_SHAREABLE_P (obj )) {
15851680 return traverse_skip ;
@@ -1600,11 +1695,9 @@ shareable_p_enter(VALUE obj)
16001695}
16011696
16021697bool
1603- rb_ractor_shareable_p_continue (VALUE obj )
1698+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16041699{
1605- if (rb_obj_traverse (obj ,
1606- shareable_p_enter , null_leave ,
1607- mark_shareable )) {
1700+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16081701 return false;
16091702 }
16101703 else {
@@ -1620,7 +1713,7 @@ rb_ractor_setup_belonging(VALUE obj)
16201713}
16211714
16221715static enum obj_traverse_iterator_result
1623- reset_belonging_enter (VALUE obj )
1716+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16241717{
16251718 if (rb_ractor_shareable_p (obj )) {
16261719 return traverse_skip ;
@@ -1642,7 +1735,7 @@ static VALUE
16421735ractor_reset_belonging (VALUE obj )
16431736{
16441737#if RACTOR_CHECK_MODE > 0
1645- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1738+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16461739#endif
16471740 return obj ;
16481741}
0 commit comments