@@ -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,13 +1223,15 @@ 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 ;
1234+ VALUE obj ;
12321235};
12331236
12341237static int obj_traverse_i (VALUE obj , struct obj_traverse_data * data );
@@ -1239,11 +1242,13 @@ obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
12391242 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12401243
12411244 if (obj_traverse_i (key , d -> data )) {
1245+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash key %+" PRIsVALUE , key );
12421246 d -> stop = true;
12431247 return ST_STOP ;
12441248 }
12451249
12461250 if (obj_traverse_i (val , d -> data )) {
1251+ rb_ractor_error_chain_append (d -> data -> chain , "\n from Hash value at key %+" PRIsVALUE , key );
12471252 d -> stop = true;
12481253 return ST_STOP ;
12491254 }
@@ -1277,6 +1282,7 @@ obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
12771282 struct obj_traverse_callback_data * d = (struct obj_traverse_callback_data * )ptr ;
12781283
12791284 if (obj_traverse_i (val , d -> data )) {
1285+ rb_ractor_error_chain_append (d -> data -> chain , "\n from instance variable %" PRIsVALUE " of an instance of %" PRIsVALUE , rb_id2str (key ), rb_class_real (CLASS_OF (d -> obj )));
12801286 d -> stop = true;
12811287 return ST_STOP ;
12821288 }
@@ -1289,7 +1295,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12891295{
12901296 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12911297
1292- switch (data -> enter_func (obj )) {
1298+ switch (data -> enter_func (obj , data )) {
12931299 case traverse_cont : break ;
12941300 case traverse_skip : return 0 ; // skip children
12951301 case traverse_stop : return 1 ; // stop search
@@ -1304,9 +1310,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13041310 struct obj_traverse_callback_data d = {
13051311 .stop = false,
13061312 .data = data ,
1313+ .obj = obj ,
13071314 };
13081315 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1309- if (d .stop ) return 1 ;
1316+ if (d .stop ) {
1317+ return 1 ;
1318+ }
13101319
13111320 switch (BUILTIN_TYPE (obj )) {
13121321 // no child node
@@ -1328,14 +1337,25 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13281337
13291338 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13301339 VALUE e = rb_ary_entry (obj , i );
1331- if (obj_traverse_i (e , data )) return 1 ;
1340+ if (obj_traverse_i (e , data )) {
1341+ rb_ractor_error_chain_append (data -> chain , "\n from Array element at index %d" , i );
1342+ return 1 ;
1343+ }
13321344 }
13331345 }
13341346 break ;
13351347
13361348 case T_HASH :
13371349 {
1338- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1350+ const VALUE ifnone = RHASH_IFNONE (obj );
1351+ if (obj_traverse_i (ifnone , data )) {
1352+ if (RB_FL_TEST_RAW (obj , RHASH_PROC_DEFAULT )) {
1353+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default proc" );
1354+ } else {
1355+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default value" );
1356+ }
1357+ return 1 ;
1358+ }
13391359
13401360 struct obj_traverse_callback_data d = {
13411361 .stop = false,
@@ -1352,7 +1372,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13521372 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13531373
13541374 for (long i = 0 ; i < len ; i ++ ) {
1355- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1375+ if (obj_traverse_i (ptr [i ], data )) {
1376+ VALUE members = rb_struct_members (obj );
1377+ VALUE member_name = rb_array_const_ptr (members )[i ];
1378+ rb_ractor_error_chain_append (data -> chain , "\n from struct member %+" PRIsVALUE , member_name );
1379+ return 1 ;
1380+ }
13561381 }
13571382 }
13581383 break ;
@@ -1423,15 +1448,21 @@ static int
14231448rb_obj_traverse (VALUE obj ,
14241449 rb_obj_traverse_enter_func enter_func ,
14251450 rb_obj_traverse_leave_func leave_func ,
1426- rb_obj_traverse_final_func final_func )
1451+ rb_obj_traverse_final_func final_func ,
1452+ VALUE * chain ,
1453+ VALUE * exception )
14271454{
14281455 struct obj_traverse_data data = {
14291456 .enter_func = enter_func ,
14301457 .leave_func = leave_func ,
14311458 .rec = NULL ,
1459+ .chain = chain ,
1460+ .exception = exception ,
14321461 };
14331462
1434- if (obj_traverse_i (obj , & data )) return 1 ;
1463+ if (obj_traverse_i (obj , & data )) {
1464+ return 1 ;
1465+ }
14351466 if (final_func && data .rec ) {
14361467 struct rb_obj_traverse_final_data f = {final_func , 0 };
14371468 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1456,14 +1487,45 @@ allow_frozen_shareable_p(VALUE obj)
14561487 return false;
14571488}
14581489
1490+ static VALUE
1491+ try_freeze (VALUE obj )
1492+ {
1493+ rb_funcall (obj , idFreeze , 0 );
1494+ return Qtrue ;
1495+ }
1496+
1497+ struct rescue_freeze_data {
1498+ VALUE exception ;
1499+ };
1500+
1501+ static VALUE
1502+ rescue_freeze (VALUE data , VALUE freeze_exception )
1503+ {
1504+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1505+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1506+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1507+ rescue_freeze_data -> exception = exception ;
1508+ return Qfalse ;
1509+ }
1510+
14591511static enum obj_traverse_iterator_result
1460- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1512+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14611513{
14621514 if (!RB_OBJ_FROZEN_RAW (obj )) {
1463- rb_funcall (obj , idFreeze , 0 );
1515+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1516+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1517+ if (data -> exception ) {
1518+ * data -> exception = rescue_freeze_data .exception ;
1519+ }
1520+ return traverse_stop ;
1521+ }
14641522
14651523 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1466- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1524+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1525+ if (data -> exception ) {
1526+ * data -> exception = exception ;
1527+ }
1528+ return traverse_stop ;
14671529 }
14681530
14691531 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1477,7 +1539,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14771539static int obj_refer_only_shareables_p (VALUE obj );
14781540
14791541static enum obj_traverse_iterator_result
1480- make_shareable_check_shareable (VALUE obj )
1542+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14811543{
14821544 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14831545
@@ -1490,7 +1552,8 @@ make_shareable_check_shareable(VALUE obj)
14901552
14911553 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14921554 if (obj_refer_only_shareables_p (obj )) {
1493- make_shareable_check_shareable_freeze (obj , traverse_skip );
1555+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1556+ if (result == traverse_stop ) return traverse_stop ;
14941557 RB_OBJ_SET_SHAREABLE (obj );
14951558 return traverse_skip ;
14961559 }
@@ -1500,11 +1563,19 @@ make_shareable_check_shareable(VALUE obj)
15001563 }
15011564 }
15021565 else if (rb_obj_is_proc (obj )) {
1503- rb_proc_ractor_make_shareable (obj , Qundef );
1566+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1567+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1568+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1569+
1570+ if (data -> exception ) {
1571+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1572+ }
1573+ return traverse_stop ;
1574+ }
15041575 return traverse_cont ;
15051576 }
15061577 else {
1507- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1578+ return traverse_stop ;
15081579 }
15091580 }
15101581
@@ -1529,7 +1600,7 @@ make_shareable_check_shareable(VALUE obj)
15291600 break ;
15301601 }
15311602
1532- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1603+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15331604}
15341605
15351606static enum obj_traverse_iterator_result
@@ -1546,9 +1617,20 @@ mark_shareable(VALUE obj)
15461617VALUE
15471618rb_ractor_make_shareable (VALUE obj )
15481619{
1549- rb_obj_traverse (obj ,
1550- make_shareable_check_shareable ,
1551- null_leave , mark_shareable );
1620+ VALUE chain = Qnil ;
1621+ VALUE exception = Qfalse ;
1622+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1623+ if (exception ) {
1624+ VALUE id_mesg = rb_intern ("mesg" );
1625+ VALUE message = rb_attr_get (exception , id_mesg );
1626+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1627+ rb_ivar_set (exception , id_mesg , message );
1628+ rb_exc_raise (exception );
1629+ }
1630+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1631+ }
1632+ RB_GC_GUARD (chain );
1633+ RB_GC_GUARD (exception );
15521634 return obj ;
15531635}
15541636
@@ -1579,7 +1661,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15791661}
15801662
15811663static enum obj_traverse_iterator_result
1582- shareable_p_enter (VALUE obj )
1664+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15831665{
15841666 if (RB_OBJ_SHAREABLE_P (obj )) {
15851667 return traverse_skip ;
@@ -1600,11 +1682,9 @@ shareable_p_enter(VALUE obj)
16001682}
16011683
16021684bool
1603- rb_ractor_shareable_p_continue (VALUE obj )
1685+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16041686{
1605- if (rb_obj_traverse (obj ,
1606- shareable_p_enter , null_leave ,
1607- mark_shareable )) {
1687+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16081688 return false;
16091689 }
16101690 else {
@@ -1620,7 +1700,7 @@ rb_ractor_setup_belonging(VALUE obj)
16201700}
16211701
16221702static enum obj_traverse_iterator_result
1623- reset_belonging_enter (VALUE obj )
1703+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16241704{
16251705 if (rb_ractor_shareable_p (obj )) {
16261706 return traverse_skip ;
@@ -1642,7 +1722,7 @@ static VALUE
16421722ractor_reset_belonging (VALUE obj )
16431723{
16441724#if RACTOR_CHECK_MODE > 0
1645- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1725+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16461726#endif
16471727 return obj ;
16481728}
0 commit comments