@@ -1216,7 +1216,8 @@ enum obj_traverse_iterator_result {
12161216 traverse_stop ,
12171217};
12181218
1219- typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj );
1219+ struct obj_traverse_data ;
1220+ typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj , struct obj_traverse_data * data );
12201221typedef enum obj_traverse_iterator_result (* rb_obj_traverse_leave_func )(VALUE obj );
12211222typedef enum obj_traverse_iterator_result (* rb_obj_traverse_final_func )(VALUE obj );
12221223
@@ -1227,13 +1228,15 @@ struct obj_traverse_data {
12271228 rb_obj_traverse_leave_func leave_func ;
12281229
12291230 st_table * rec ;
1230- VALUE rec_hash ;
1231+ VALUE rec_hash ; // objects seen during traversal
1232+ VALUE * chain ; // reference chain string built during unwinding (NULL if not needed)
1233+ VALUE * exception ; // exception raised trying to freeze an object
12311234};
12321235
1233-
12341236struct obj_traverse_callback_data {
12351237 bool stop ;
12361238 struct obj_traverse_data * data ;
1239+ VALUE obj ;
12371240};
12381241
12391242static int obj_traverse_i (VALUE obj , struct obj_traverse_data * data );
@@ -1244,11 +1247,13 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
12441247 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12451248
12461249 if (obj_traverse_i (key , d -> data )) {
1250+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash key %+" PRIsVALUE , key );
12471251 d -> stop = true;
12481252 return ST_STOP ;
12491253 }
12501254
12511255 if (obj_traverse_i (val , d -> data )) {
1256+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash value at key %+" PRIsVALUE , key );
12521257 d -> stop = true;
12531258 return ST_STOP ;
12541259 }
@@ -1282,6 +1287,9 @@ obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
12821287 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12831288
12841289 if (obj_traverse_i (val , d -> data )) {
1290+ rb_ractor_error_chain_append (d -> data -> chain ,
1291+ "\n from instance variable %" PRIsVALUE " of an instance of %" PRIsVALUE ,
1292+ rb_id2str (key ), rb_class_real (CLASS_OF (d -> obj )));
12851293 d -> stop = true;
12861294 return ST_STOP ;
12871295 }
@@ -1294,7 +1302,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12941302{
12951303 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12961304
1297- switch (data -> enter_func (obj )) {
1305+ switch (data -> enter_func (obj , data )) {
12981306 case traverse_cont : break ;
12991307 case traverse_skip : return 0 ; // skip children
13001308 case traverse_stop : return 1 ; // stop search
@@ -1309,9 +1317,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13091317 struct obj_traverse_callback_data d = {
13101318 .stop = false,
13111319 .data = data ,
1320+ .obj = obj ,
13121321 };
13131322 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1314- if (d .stop ) return 1 ;
1323+ if (d .stop ) {
1324+ return 1 ;
1325+ }
13151326
13161327 switch (BUILTIN_TYPE (obj )) {
13171328 // no child node
@@ -1333,14 +1344,26 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13331344
13341345 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13351346 VALUE e = rb_ary_entry (obj , i );
1336- if (obj_traverse_i (e , data )) return 1 ;
1347+ if (obj_traverse_i (e , data )) {
1348+ rb_ractor_error_chain_append (data -> chain , "\n from Array element at index %d" , i );
1349+ return 1 ;
1350+ }
13371351 }
13381352 }
13391353 break ;
13401354
13411355 case T_HASH :
13421356 {
1343- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1357+ const VALUE ifnone = RHASH_IFNONE (obj );
1358+ if (obj_traverse_i (ifnone , data )) {
1359+ if (RB_FL_TEST_RAW (obj , RHASH_PROC_DEFAULT )) {
1360+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default proc" );
1361+ }
1362+ else {
1363+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default value" );
1364+ }
1365+ return 1 ;
1366+ }
13441367
13451368 struct obj_traverse_callback_data d = {
13461369 .stop = false,
@@ -1357,7 +1380,14 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13571380 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13581381
13591382 for (long i = 0 ; i < len ; i ++ ) {
1360- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1383+ if (obj_traverse_i (ptr [i ], data )) {
1384+ VALUE members = rb_struct_members (obj );
1385+ VALUE member_name = rb_array_const_ptr (members )[i ];
1386+ rb_ractor_error_chain_append (data -> chain ,
1387+ "\n from member %+" PRIsVALUE " of an instance of %" PRIsVALUE ,
1388+ member_name , rb_class_real (CLASS_OF (obj )));
1389+ return 1 ;
1390+ }
13611391 }
13621392 }
13631393 break ;
@@ -1428,15 +1458,21 @@ static int
14281458rb_obj_traverse (VALUE obj ,
14291459 rb_obj_traverse_enter_func enter_func ,
14301460 rb_obj_traverse_leave_func leave_func ,
1431- rb_obj_traverse_final_func final_func )
1461+ rb_obj_traverse_final_func final_func ,
1462+ VALUE * chain ,
1463+ VALUE * exception )
14321464{
14331465 struct obj_traverse_data data = {
14341466 .enter_func = enter_func ,
14351467 .leave_func = leave_func ,
14361468 .rec = NULL ,
1469+ .chain = chain ,
1470+ .exception = exception ,
14371471 };
14381472
1439- if (obj_traverse_i (obj , & data )) return 1 ;
1473+ if (obj_traverse_i (obj , & data )) {
1474+ return 1 ;
1475+ }
14401476 if (final_func && data .rec ) {
14411477 struct rb_obj_traverse_final_data f = {final_func , 0 };
14421478 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1461,14 +1497,45 @@ allow_frozen_shareable_p(VALUE obj)
14611497 return false;
14621498}
14631499
1500+ static VALUE
1501+ try_freeze (VALUE obj )
1502+ {
1503+ rb_funcall (obj , idFreeze , 0 );
1504+ return Qtrue ;
1505+ }
1506+
1507+ struct rescue_freeze_data {
1508+ VALUE exception ;
1509+ };
1510+
1511+ static VALUE
1512+ rescue_freeze (VALUE data , VALUE freeze_exception )
1513+ {
1514+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1515+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1516+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1517+ rescue_freeze_data -> exception = exception ;
1518+ return Qfalse ;
1519+ }
1520+
14641521static enum obj_traverse_iterator_result
1465- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1522+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14661523{
14671524 if (!RB_OBJ_FROZEN_RAW (obj )) {
1468- rb_funcall (obj , idFreeze , 0 );
1525+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1526+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1527+ if (data -> exception ) {
1528+ * data -> exception = rescue_freeze_data .exception ;
1529+ }
1530+ return traverse_stop ;
1531+ }
14691532
14701533 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1471- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1534+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1535+ if (data -> exception ) {
1536+ * data -> exception = exception ;
1537+ }
1538+ return traverse_stop ;
14721539 }
14731540
14741541 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1482,7 +1549,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14821549static int obj_refer_only_shareables_p (VALUE obj );
14831550
14841551static enum obj_traverse_iterator_result
1485- make_shareable_check_shareable (VALUE obj )
1552+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14861553{
14871554 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14881555
@@ -1495,7 +1562,8 @@ make_shareable_check_shareable(VALUE obj)
14951562
14961563 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14971564 if (obj_refer_only_shareables_p (obj )) {
1498- make_shareable_check_shareable_freeze (obj , traverse_skip );
1565+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1566+ if (result == traverse_stop ) return traverse_stop ;
14991567 RB_OBJ_SET_SHAREABLE (obj );
15001568 return traverse_skip ;
15011569 }
@@ -1505,11 +1573,19 @@ make_shareable_check_shareable(VALUE obj)
15051573 }
15061574 }
15071575 else if (rb_obj_is_proc (obj )) {
1508- rb_proc_ractor_make_shareable (obj , Qundef );
1576+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1577+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1578+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1579+
1580+ if (data -> exception ) {
1581+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1582+ }
1583+ return traverse_stop ;
1584+ }
15091585 return traverse_cont ;
15101586 }
15111587 else {
1512- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1588+ return traverse_stop ;
15131589 }
15141590 }
15151591
@@ -1534,7 +1610,7 @@ make_shareable_check_shareable(VALUE obj)
15341610 break ;
15351611 }
15361612
1537- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1613+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15381614}
15391615
15401616static enum obj_traverse_iterator_result
@@ -1551,9 +1627,20 @@ mark_shareable(VALUE obj)
15511627VALUE
15521628rb_ractor_make_shareable (VALUE obj )
15531629{
1554- rb_obj_traverse (obj ,
1555- make_shareable_check_shareable ,
1556- null_leave , mark_shareable );
1630+ VALUE chain = Qnil ;
1631+ VALUE exception = Qfalse ;
1632+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1633+ if (exception ) {
1634+ VALUE id_mesg = rb_intern ("mesg" );
1635+ VALUE message = rb_attr_get (exception , id_mesg );
1636+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1637+ rb_ivar_set (exception , id_mesg , message );
1638+ rb_exc_raise (exception );
1639+ }
1640+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1641+ }
1642+ RB_GC_GUARD (chain );
1643+ RB_GC_GUARD (exception );
15571644 return obj ;
15581645}
15591646
@@ -1584,7 +1671,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15841671}
15851672
15861673static enum obj_traverse_iterator_result
1587- shareable_p_enter (VALUE obj )
1674+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15881675{
15891676 if (RB_OBJ_SHAREABLE_P (obj )) {
15901677 return traverse_skip ;
@@ -1605,11 +1692,9 @@ shareable_p_enter(VALUE obj)
16051692}
16061693
16071694bool
1608- rb_ractor_shareable_p_continue (VALUE obj )
1695+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16091696{
1610- if (rb_obj_traverse (obj ,
1611- shareable_p_enter , null_leave ,
1612- mark_shareable )) {
1697+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16131698 return false;
16141699 }
16151700 else {
@@ -1625,7 +1710,7 @@ rb_ractor_setup_belonging(VALUE obj)
16251710}
16261711
16271712static enum obj_traverse_iterator_result
1628- reset_belonging_enter (VALUE obj )
1713+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16291714{
16301715 if (rb_ractor_shareable_p (obj )) {
16311716 return traverse_skip ;
@@ -1647,7 +1732,7 @@ static VALUE
16471732ractor_reset_belonging (VALUE obj )
16481733{
16491734#if RACTOR_CHECK_MODE > 0
1650- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1735+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16511736#endif
16521737 return obj ;
16531738}
0 commit comments