@@ -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,10 +1223,11 @@ 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 ;
@@ -1239,11 +1241,13 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
12391241 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12401242
12411243 if (obj_traverse_i (key , d -> data )) {
1244+ rb_ractor_error_chain_append (d -> data -> chain , "\n from hash key %+" PRIsVALUE , key );
12421245 d -> stop = true;
12431246 return ST_STOP ;
12441247 }
12451248
12461249 if (obj_traverse_i (val , d -> data )) {
1250+ rb_ractor_error_chain_append (d -> data -> chain , "\n from hash value at key %+" PRIsVALUE , key );
12471251 d -> stop = true;
12481252 return ST_STOP ;
12491253 }
@@ -1277,6 +1281,7 @@ obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
12771281 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12781282
12791283 if (obj_traverse_i (val , d -> data )) {
1284+ rb_ractor_error_chain_append (d -> data -> chain , "\n from instance variable %" PRIsVALUE , rb_id2str (key ));
12801285 d -> stop = true;
12811286 return ST_STOP ;
12821287 }
@@ -1289,7 +1294,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12891294{
12901295 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12911296
1292- switch (data -> enter_func (obj )) {
1297+ switch (data -> enter_func (obj , data )) {
12931298 case traverse_cont : break ;
12941299 case traverse_skip : return 0 ; // skip children
12951300 case traverse_stop : return 1 ; // stop search
@@ -1306,7 +1311,9 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13061311 .data = data ,
13071312 };
13081313 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1309- if (d .stop ) return 1 ;
1314+ if (d .stop ) {
1315+ return 1 ;
1316+ }
13101317
13111318 switch (BUILTIN_TYPE (obj )) {
13121319 // no child node
@@ -1328,14 +1335,21 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13281335
13291336 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13301337 VALUE e = rb_ary_entry (obj , i );
1331- if (obj_traverse_i (e , data )) return 1 ;
1338+ if (obj_traverse_i (e , data )) {
1339+ rb_ractor_error_chain_append (data -> chain , "\n from array element at index %d" , i );
1340+ return 1 ;
1341+ }
13321342 }
13331343 }
13341344 break ;
13351345
13361346 case T_HASH :
13371347 {
1338- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1348+ const VALUE ifnone = RHASH_IFNONE (obj );
1349+ if (obj_traverse_i (ifnone , data )) {
1350+ rb_ractor_error_chain_append (data -> chain , "\n from hash default value" );
1351+ return 1 ;
1352+ }
13391353
13401354 struct obj_traverse_callback_data d = {
13411355 .stop = false,
@@ -1352,7 +1366,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13521366 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13531367
13541368 for (long i = 0 ; i < len ; i ++ ) {
1355- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1369+ if (obj_traverse_i (ptr [i ], data )) {
1370+ VALUE members = rb_struct_members (obj );
1371+ VALUE member_name = rb_array_const_ptr (members )[i ];
1372+ rb_ractor_error_chain_append (data -> chain , "\n from struct member %+" PRIsVALUE , member_name );
1373+ return 1 ;
1374+ }
13561375 }
13571376 }
13581377 break ;
@@ -1423,15 +1442,21 @@ static int
14231442rb_obj_traverse (VALUE obj ,
14241443 rb_obj_traverse_enter_func enter_func ,
14251444 rb_obj_traverse_leave_func leave_func ,
1426- rb_obj_traverse_final_func final_func )
1445+ rb_obj_traverse_final_func final_func ,
1446+ VALUE * chain ,
1447+ VALUE * exception )
14271448{
14281449 struct obj_traverse_data data = {
14291450 .enter_func = enter_func ,
14301451 .leave_func = leave_func ,
14311452 .rec = NULL ,
1453+ .chain = chain ,
1454+ .exception = exception ,
14321455 };
14331456
1434- if (obj_traverse_i (obj , & data )) return 1 ;
1457+ if (obj_traverse_i (obj , & data )) {
1458+ return 1 ;
1459+ }
14351460 if (final_func && data .rec ) {
14361461 struct rb_obj_traverse_final_data f = {final_func , 0 };
14371462 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1456,14 +1481,45 @@ allow_frozen_shareable_p(VALUE obj)
14561481 return false;
14571482}
14581483
1484+ static VALUE
1485+ try_freeze (VALUE obj )
1486+ {
1487+ rb_funcall (obj , idFreeze , 0 );
1488+ return Qtrue ;
1489+ }
1490+
1491+ struct rescue_freeze_data {
1492+ VALUE exception ;
1493+ };
1494+
1495+ static VALUE
1496+ rescue_freeze (VALUE data , VALUE freeze_exception )
1497+ {
1498+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1499+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1500+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1501+ rescue_freeze_data -> exception = exception ;
1502+ return Qfalse ;
1503+ }
1504+
14591505static enum obj_traverse_iterator_result
1460- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1506+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14611507{
14621508 if (!RB_OBJ_FROZEN_RAW (obj )) {
1463- rb_funcall (obj , idFreeze , 0 );
1509+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1510+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1511+ if (data -> exception ) {
1512+ * data -> exception = rescue_freeze_data .exception ;
1513+ }
1514+ return traverse_stop ;
1515+ }
14641516
14651517 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1466- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1518+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1519+ if (data -> exception ) {
1520+ * data -> exception = exception ;
1521+ }
1522+ return traverse_stop ;
14671523 }
14681524
14691525 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1477,7 +1533,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14771533static int obj_refer_only_shareables_p (VALUE obj );
14781534
14791535static enum obj_traverse_iterator_result
1480- make_shareable_check_shareable (VALUE obj )
1536+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14811537{
14821538 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14831539
@@ -1490,7 +1546,8 @@ make_shareable_check_shareable(VALUE obj)
14901546
14911547 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14921548 if (obj_refer_only_shareables_p (obj )) {
1493- make_shareable_check_shareable_freeze (obj , traverse_skip );
1549+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1550+ if (result == traverse_stop ) return traverse_stop ;
14941551 RB_OBJ_SET_SHAREABLE (obj );
14951552 return traverse_skip ;
14961553 }
@@ -1500,11 +1557,19 @@ make_shareable_check_shareable(VALUE obj)
15001557 }
15011558 }
15021559 else if (rb_obj_is_proc (obj )) {
1503- rb_proc_ractor_make_shareable (obj , Qundef );
1560+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1561+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1562+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1563+
1564+ if (data -> exception ) {
1565+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1566+ }
1567+ return traverse_stop ;
1568+ }
15041569 return traverse_cont ;
15051570 }
15061571 else {
1507- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1572+ return traverse_stop ;
15081573 }
15091574 }
15101575
@@ -1529,7 +1594,7 @@ make_shareable_check_shareable(VALUE obj)
15291594 break ;
15301595 }
15311596
1532- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1597+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15331598}
15341599
15351600static enum obj_traverse_iterator_result
@@ -1546,9 +1611,20 @@ mark_shareable(VALUE obj)
15461611VALUE
15471612rb_ractor_make_shareable (VALUE obj )
15481613{
1549- rb_obj_traverse (obj ,
1550- make_shareable_check_shareable ,
1551- null_leave , mark_shareable );
1614+ VALUE chain = Qnil ;
1615+ VALUE exception = Qfalse ;
1616+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1617+ if (exception ) {
1618+ VALUE id_mesg = rb_intern ("mesg" );
1619+ VALUE message = rb_attr_get (exception , id_mesg );
1620+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1621+ rb_ivar_set (exception , id_mesg , message );
1622+ rb_exc_raise (exception );
1623+ }
1624+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1625+ }
1626+ RB_GC_GUARD (chain );
1627+ RB_GC_GUARD (exception );
15521628 return obj ;
15531629}
15541630
@@ -1579,7 +1655,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15791655}
15801656
15811657static enum obj_traverse_iterator_result
1582- shareable_p_enter (VALUE obj )
1658+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15831659{
15841660 if (RB_OBJ_SHAREABLE_P (obj )) {
15851661 return traverse_skip ;
@@ -1600,11 +1676,9 @@ shareable_p_enter(VALUE obj)
16001676}
16011677
16021678bool
1603- rb_ractor_shareable_p_continue (VALUE obj )
1679+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16041680{
1605- if (rb_obj_traverse (obj ,
1606- shareable_p_enter , null_leave ,
1607- mark_shareable )) {
1681+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16081682 return false;
16091683 }
16101684 else {
@@ -1620,7 +1694,7 @@ rb_ractor_setup_belonging(VALUE obj)
16201694}
16211695
16221696static enum obj_traverse_iterator_result
1623- reset_belonging_enter (VALUE obj )
1697+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16241698{
16251699 if (rb_ractor_shareable_p (obj )) {
16261700 return traverse_skip ;
@@ -1642,7 +1716,7 @@ static VALUE
16421716ractor_reset_belonging (VALUE obj )
16431717{
16441718#if RACTOR_CHECK_MODE > 0
1645- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1719+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16461720#endif
16471721 return obj ;
16481722}
0 commit comments