@@ -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,9 @@ 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 ,
1286+ "\n from instance variable %" PRIsVALUE " of an instance of %" PRIsVALUE ,
1287+ rb_id2str (key ), rb_class_real (CLASS_OF (d -> obj )));
12801288 d -> stop = true;
12811289 return ST_STOP ;
12821290 }
@@ -1289,7 +1297,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12891297{
12901298 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12911299
1292- switch (data -> enter_func (obj )) {
1300+ switch (data -> enter_func (obj , data )) {
12931301 case traverse_cont : break ;
12941302 case traverse_skip : return 0 ; // skip children
12951303 case traverse_stop : return 1 ; // stop search
@@ -1304,9 +1312,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13041312 struct obj_traverse_callback_data d = {
13051313 .stop = false,
13061314 .data = data ,
1315+ .obj = obj ,
13071316 };
13081317 rb_ivar_foreach (obj , obj_traverse_ivar_foreach_i , (st_data_t )& d );
1309- if (d .stop ) return 1 ;
1318+ if (d .stop ) {
1319+ return 1 ;
1320+ }
13101321
13111322 switch (BUILTIN_TYPE (obj )) {
13121323 // no child node
@@ -1328,14 +1339,26 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13281339
13291340 for (int i = 0 ; i < RARRAY_LENINT (obj ); i ++ ) {
13301341 VALUE e = rb_ary_entry (obj , i );
1331- if (obj_traverse_i (e , data )) return 1 ;
1342+ if (obj_traverse_i (e , data )) {
1343+ rb_ractor_error_chain_append (data -> chain , "\n from Array element at index %d" , i );
1344+ return 1 ;
1345+ }
13321346 }
13331347 }
13341348 break ;
13351349
13361350 case T_HASH :
13371351 {
1338- if (obj_traverse_i (RHASH_IFNONE (obj ), data )) return 1 ;
1352+ const VALUE ifnone = RHASH_IFNONE (obj );
1353+ if (obj_traverse_i (ifnone , data )) {
1354+ if (RB_FL_TEST_RAW (obj , RHASH_PROC_DEFAULT )) {
1355+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default proc" );
1356+ }
1357+ else {
1358+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default value" );
1359+ }
1360+ return 1 ;
1361+ }
13391362
13401363 struct obj_traverse_callback_data d = {
13411364 .stop = false,
@@ -1352,7 +1375,14 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13521375 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13531376
13541377 for (long i = 0 ; i < len ; i ++ ) {
1355- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1378+ if (obj_traverse_i (ptr [i ], data )) {
1379+ VALUE members = rb_struct_members (obj );
1380+ VALUE member_name = rb_array_const_ptr (members )[i ];
1381+ rb_ractor_error_chain_append (data -> chain ,
1382+ "\n from member %+" PRIsVALUE " of an instance of %" PRIsVALUE ,
1383+ member_name , rb_class_real (CLASS_OF (obj )));
1384+ return 1 ;
1385+ }
13561386 }
13571387 }
13581388 break ;
@@ -1423,15 +1453,21 @@ static int
14231453rb_obj_traverse (VALUE obj ,
14241454 rb_obj_traverse_enter_func enter_func ,
14251455 rb_obj_traverse_leave_func leave_func ,
1426- rb_obj_traverse_final_func final_func )
1456+ rb_obj_traverse_final_func final_func ,
1457+ VALUE * chain ,
1458+ VALUE * exception )
14271459{
14281460 struct obj_traverse_data data = {
14291461 .enter_func = enter_func ,
14301462 .leave_func = leave_func ,
14311463 .rec = NULL ,
1464+ .chain = chain ,
1465+ .exception = exception ,
14321466 };
14331467
1434- if (obj_traverse_i (obj , & data )) return 1 ;
1468+ if (obj_traverse_i (obj , & data )) {
1469+ return 1 ;
1470+ }
14351471 if (final_func && data .rec ) {
14361472 struct rb_obj_traverse_final_data f = {final_func , 0 };
14371473 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1456,14 +1492,45 @@ allow_frozen_shareable_p(VALUE obj)
14561492 return false;
14571493}
14581494
1495+ static VALUE
1496+ try_freeze (VALUE obj )
1497+ {
1498+ rb_funcall (obj , idFreeze , 0 );
1499+ return Qtrue ;
1500+ }
1501+
1502+ struct rescue_freeze_data {
1503+ VALUE exception ;
1504+ };
1505+
1506+ static VALUE
1507+ rescue_freeze (VALUE data , VALUE freeze_exception )
1508+ {
1509+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1510+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1511+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1512+ rescue_freeze_data -> exception = exception ;
1513+ return Qfalse ;
1514+ }
1515+
14591516static enum obj_traverse_iterator_result
1460- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1517+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14611518{
14621519 if (!RB_OBJ_FROZEN_RAW (obj )) {
1463- rb_funcall (obj , idFreeze , 0 );
1520+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1521+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1522+ if (data -> exception ) {
1523+ * data -> exception = rescue_freeze_data .exception ;
1524+ }
1525+ return traverse_stop ;
1526+ }
14641527
14651528 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1466- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1529+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1530+ if (data -> exception ) {
1531+ * data -> exception = exception ;
1532+ }
1533+ return traverse_stop ;
14671534 }
14681535
14691536 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1477,7 +1544,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14771544static int obj_refer_only_shareables_p (VALUE obj );
14781545
14791546static enum obj_traverse_iterator_result
1480- make_shareable_check_shareable (VALUE obj )
1547+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14811548{
14821549 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14831550
@@ -1490,7 +1557,8 @@ make_shareable_check_shareable(VALUE obj)
14901557
14911558 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14921559 if (obj_refer_only_shareables_p (obj )) {
1493- make_shareable_check_shareable_freeze (obj , traverse_skip );
1560+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1561+ if (result == traverse_stop ) return traverse_stop ;
14941562 RB_OBJ_SET_SHAREABLE (obj );
14951563 return traverse_skip ;
14961564 }
@@ -1500,11 +1568,19 @@ make_shareable_check_shareable(VALUE obj)
15001568 }
15011569 }
15021570 else if (rb_obj_is_proc (obj )) {
1503- rb_proc_ractor_make_shareable (obj , Qundef );
1571+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1572+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1573+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1574+
1575+ if (data -> exception ) {
1576+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1577+ }
1578+ return traverse_stop ;
1579+ }
15041580 return traverse_cont ;
15051581 }
15061582 else {
1507- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1583+ return traverse_stop ;
15081584 }
15091585 }
15101586
@@ -1529,7 +1605,7 @@ make_shareable_check_shareable(VALUE obj)
15291605 break ;
15301606 }
15311607
1532- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1608+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15331609}
15341610
15351611static enum obj_traverse_iterator_result
@@ -1546,9 +1622,20 @@ mark_shareable(VALUE obj)
15461622VALUE
15471623rb_ractor_make_shareable (VALUE obj )
15481624{
1549- rb_obj_traverse (obj ,
1550- make_shareable_check_shareable ,
1551- null_leave , mark_shareable );
1625+ VALUE chain = Qnil ;
1626+ VALUE exception = Qfalse ;
1627+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1628+ if (exception ) {
1629+ VALUE id_mesg = rb_intern ("mesg" );
1630+ VALUE message = rb_attr_get (exception , id_mesg );
1631+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1632+ rb_ivar_set (exception , id_mesg , message );
1633+ rb_exc_raise (exception );
1634+ }
1635+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1636+ }
1637+ RB_GC_GUARD (chain );
1638+ RB_GC_GUARD (exception );
15521639 return obj ;
15531640}
15541641
@@ -1579,7 +1666,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15791666}
15801667
15811668static enum obj_traverse_iterator_result
1582- shareable_p_enter (VALUE obj )
1669+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15831670{
15841671 if (RB_OBJ_SHAREABLE_P (obj )) {
15851672 return traverse_skip ;
@@ -1600,11 +1687,9 @@ shareable_p_enter(VALUE obj)
16001687}
16011688
16021689bool
1603- rb_ractor_shareable_p_continue (VALUE obj )
1690+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16041691{
1605- if (rb_obj_traverse (obj ,
1606- shareable_p_enter , null_leave ,
1607- mark_shareable )) {
1692+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16081693 return false;
16091694 }
16101695 else {
@@ -1620,7 +1705,7 @@ rb_ractor_setup_belonging(VALUE obj)
16201705}
16211706
16221707static enum obj_traverse_iterator_result
1623- reset_belonging_enter (VALUE obj )
1708+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16241709{
16251710 if (rb_ractor_shareable_p (obj )) {
16261711 return traverse_skip ;
@@ -1642,7 +1727,7 @@ static VALUE
16421727ractor_reset_belonging (VALUE obj )
16431728{
16441729#if RACTOR_CHECK_MODE > 0
1645- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1730+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16461731#endif
16471732 return obj ;
16481733}
0 commit comments