@@ -1215,7 +1215,8 @@ enum obj_traverse_iterator_result {
12151215 traverse_stop ,
12161216};
12171217
1218- typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj );
1218+ struct obj_traverse_data ;
1219+ typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj , struct obj_traverse_data * data );
12191220typedef enum obj_traverse_iterator_result (* rb_obj_traverse_leave_func )(VALUE obj );
12201221typedef enum obj_traverse_iterator_result (* rb_obj_traverse_final_func )(VALUE obj );
12211222
@@ -1226,13 +1227,15 @@ struct obj_traverse_data {
12261227 rb_obj_traverse_leave_func leave_func ;
12271228
12281229 st_table * rec ;
1229- VALUE rec_hash ;
1230+ VALUE rec_hash ; // objects seen during traversal
1231+ VALUE * chain ; // reference chain string built during unwinding (NULL if not needed)
1232+ VALUE * exception ; // exception raised trying to freeze an object
12301233};
12311234
1232-
12331235struct obj_traverse_callback_data {
12341236 bool stop ;
12351237 struct obj_traverse_data * data ;
1238+ VALUE obj ;
12361239};
12371240
12381241static int obj_traverse_i (VALUE obj , struct obj_traverse_data * data );
@@ -1243,11 +1246,13 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
12431246 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12441247
12451248 if (obj_traverse_i (key , d -> data )) {
1249+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash key %+" PRIsVALUE , key );
12461250 d -> stop = true;
12471251 return ST_STOP ;
12481252 }
12491253
12501254 if (obj_traverse_i (val , d -> data )) {
1255+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash value at key %+" PRIsVALUE , key );
12511256 d -> stop = true;
12521257 return ST_STOP ;
12531258 }
@@ -1281,6 +1286,9 @@ obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
12811286 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12821287
12831288 if (obj_traverse_i (val , d -> data )) {
1289+ rb_ractor_error_chain_append (d -> data -> chain ,
1290+ "\n from instance variable %" PRIsVALUE " of an instance of %" PRIsVALUE ,
1291+ rb_id2str (key ), rb_class_real (CLASS_OF (d -> obj )));
12841292 d -> stop = true;
12851293 return ST_STOP ;
12861294 }
@@ -1293,7 +1301,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12931301{
12941302 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12951303
1296- switch (data -> enter_func (obj )) {
1304+ switch (data -> enter_func (obj , data )) {
12971305 case traverse_cont : break ;
12981306 case traverse_skip : return 0 ; // skip children
12991307 case traverse_stop : return 1 ; // stop search
@@ -1308,9 +1316,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13081316 struct obj_traverse_callback_data d = {
13091317 .stop = false,
13101318 .data = data ,
1319+ .obj = obj ,
13111320 };
13121321 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1313- if (d .stop ) return 1 ;
1322+ if (d .stop ) {
1323+ return 1 ;
1324+ }
13141325
13151326 switch (BUILTIN_TYPE (obj )) {
13161327 // no child node
@@ -1332,14 +1343,26 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13321343
13331344 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13341345 VALUE e = rb_ary_entry (obj , i );
1335- if (obj_traverse_i (e , data )) return 1 ;
1346+ if (obj_traverse_i (e , data )) {
1347+ rb_ractor_error_chain_append (data -> chain , "\n from Array element at index %d" , i );
1348+ return 1 ;
1349+ }
13361350 }
13371351 }
13381352 break ;
13391353
13401354 case T_HASH :
13411355 {
1342- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1356+ const VALUE ifnone = RHASH_IFNONE (obj );
1357+ if (obj_traverse_i (ifnone , data )) {
1358+ if (RB_FL_TEST_RAW (obj , RHASH_PROC_DEFAULT )) {
1359+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default proc" );
1360+ }
1361+ else {
1362+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default value" );
1363+ }
1364+ return 1 ;
1365+ }
13431366
13441367 struct obj_traverse_callback_data d = {
13451368 .stop = false,
@@ -1356,7 +1379,14 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13561379 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13571380
13581381 for (long i = 0 ; i < len ; i ++ ) {
1359- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1382+ if (obj_traverse_i (ptr [i ], data )) {
1383+ VALUE members = rb_struct_members (obj );
1384+ VALUE member_name = rb_array_const_ptr (members )[i ];
1385+ rb_ractor_error_chain_append (data -> chain ,
1386+ "\n from member %+" PRIsVALUE " of an instance of %" PRIsVALUE ,
1387+ member_name , rb_class_real (CLASS_OF (obj )));
1388+ return 1 ;
1389+ }
13601390 }
13611391 }
13621392 break ;
@@ -1427,15 +1457,21 @@ static int
14271457rb_obj_traverse (VALUE obj ,
14281458 rb_obj_traverse_enter_func enter_func ,
14291459 rb_obj_traverse_leave_func leave_func ,
1430- rb_obj_traverse_final_func final_func )
1460+ rb_obj_traverse_final_func final_func ,
1461+ VALUE * chain ,
1462+ VALUE * exception )
14311463{
14321464 struct obj_traverse_data data = {
14331465 .enter_func = enter_func ,
14341466 .leave_func = leave_func ,
14351467 .rec = NULL ,
1468+ .chain = chain ,
1469+ .exception = exception ,
14361470 };
14371471
1438- if (obj_traverse_i (obj , & data )) return 1 ;
1472+ if (obj_traverse_i (obj , & data )) {
1473+ return 1 ;
1474+ }
14391475 if (final_func && data .rec ) {
14401476 struct rb_obj_traverse_final_data f = {final_func , 0 };
14411477 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1460,14 +1496,45 @@ allow_frozen_shareable_p(VALUE obj)
14601496 return false;
14611497}
14621498
1499+ static VALUE
1500+ try_freeze (VALUE obj )
1501+ {
1502+ rb_funcall (obj , idFreeze , 0 );
1503+ return Qtrue ;
1504+ }
1505+
1506+ struct rescue_freeze_data {
1507+ VALUE exception ;
1508+ };
1509+
1510+ static VALUE
1511+ rescue_freeze (VALUE data , VALUE freeze_exception )
1512+ {
1513+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1514+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1515+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1516+ rescue_freeze_data -> exception = exception ;
1517+ return Qfalse ;
1518+ }
1519+
14631520static enum obj_traverse_iterator_result
1464- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1521+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14651522{
14661523 if (!RB_OBJ_FROZEN_RAW (obj )) {
1467- rb_funcall (obj , idFreeze , 0 );
1524+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1525+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1526+ if (data -> exception ) {
1527+ * data -> exception = rescue_freeze_data .exception ;
1528+ }
1529+ return traverse_stop ;
1530+ }
14681531
14691532 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1470- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1533+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1534+ if (data -> exception ) {
1535+ * data -> exception = exception ;
1536+ }
1537+ return traverse_stop ;
14711538 }
14721539
14731540 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1481,7 +1548,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14811548static int obj_refer_only_shareables_p (VALUE obj );
14821549
14831550static enum obj_traverse_iterator_result
1484- make_shareable_check_shareable (VALUE obj )
1551+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14851552{
14861553 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14871554
@@ -1494,7 +1561,8 @@ make_shareable_check_shareable(VALUE obj)
14941561
14951562 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14961563 if (obj_refer_only_shareables_p (obj )) {
1497- make_shareable_check_shareable_freeze (obj , traverse_skip );
1564+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1565+ if (result == traverse_stop ) return traverse_stop ;
14981566 RB_OBJ_SET_SHAREABLE (obj );
14991567 return traverse_skip ;
15001568 }
@@ -1504,11 +1572,19 @@ make_shareable_check_shareable(VALUE obj)
15041572 }
15051573 }
15061574 else if (rb_obj_is_proc (obj )) {
1507- rb_proc_ractor_make_shareable (obj , Qundef );
1575+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1576+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1577+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1578+
1579+ if (data -> exception ) {
1580+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1581+ }
1582+ return traverse_stop ;
1583+ }
15081584 return traverse_cont ;
15091585 }
15101586 else {
1511- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1587+ return traverse_stop ;
15121588 }
15131589 }
15141590
@@ -1533,7 +1609,7 @@ make_shareable_check_shareable(VALUE obj)
15331609 break ;
15341610 }
15351611
1536- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1612+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15371613}
15381614
15391615static enum obj_traverse_iterator_result
@@ -1550,9 +1626,20 @@ mark_shareable(VALUE obj)
15501626VALUE
15511627rb_ractor_make_shareable (VALUE obj )
15521628{
1553- rb_obj_traverse (obj ,
1554- make_shareable_check_shareable ,
1555- null_leave , mark_shareable );
1629+ VALUE chain = Qnil ;
1630+ VALUE exception = Qfalse ;
1631+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1632+ if (exception ) {
1633+ VALUE id_mesg = rb_intern ("mesg" );
1634+ VALUE message = rb_attr_get (exception , id_mesg );
1635+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1636+ rb_ivar_set (exception , id_mesg , message );
1637+ rb_exc_raise (exception );
1638+ }
1639+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1640+ }
1641+ RB_GC_GUARD (chain );
1642+ RB_GC_GUARD (exception );
15561643 return obj ;
15571644}
15581645
@@ -1583,7 +1670,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15831670}
15841671
15851672static enum obj_traverse_iterator_result
1586- shareable_p_enter (VALUE obj )
1673+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15871674{
15881675 if (RB_OBJ_SHAREABLE_P (obj )) {
15891676 return traverse_skip ;
@@ -1604,11 +1691,9 @@ shareable_p_enter(VALUE obj)
16041691}
16051692
16061693bool
1607- rb_ractor_shareable_p_continue (VALUE obj )
1694+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16081695{
1609- if (rb_obj_traverse (obj ,
1610- shareable_p_enter , null_leave ,
1611- mark_shareable )) {
1696+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16121697 return false;
16131698 }
16141699 else {
@@ -1624,7 +1709,7 @@ rb_ractor_setup_belonging(VALUE obj)
16241709}
16251710
16261711static enum obj_traverse_iterator_result
1627- reset_belonging_enter (VALUE obj )
1712+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16281713{
16291714 if (rb_ractor_shareable_p (obj )) {
16301715 return traverse_skip ;
@@ -1646,7 +1731,7 @@ static VALUE
16461731ractor_reset_belonging (VALUE obj )
16471732{
16481733#if RACTOR_CHECK_MODE > 0
1649- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1734+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16501735#endif
16511736 return obj ;
16521737}
0 commit comments