@@ -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,26 @@ 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+ }
1355+ else {
1356+ rb_ractor_error_chain_append (data -> chain , "\n from Hash default value" );
1357+ }
1358+ return 1 ;
1359+ }
13391360
13401361 struct obj_traverse_callback_data d = {
13411362 .stop = false,
@@ -1352,7 +1373,12 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
13521373 const VALUE * ptr = RSTRUCT_CONST_PTR (obj );
13531374
13541375 for (long i = 0 ; i < len ; i ++ ) {
1355- if (obj_traverse_i (ptr [i ], data )) return 1 ;
1376+ if (obj_traverse_i (ptr [i ], data )) {
1377+ VALUE members = rb_struct_members (obj );
1378+ VALUE member_name = rb_array_const_ptr (members )[i ];
1379+ rb_ractor_error_chain_append (data -> chain , "\n from struct member %+" PRIsVALUE , member_name );
1380+ return 1 ;
1381+ }
13561382 }
13571383 }
13581384 break ;
@@ -1423,15 +1449,21 @@ static int
14231449rb_obj_traverse (VALUE obj ,
14241450 rb_obj_traverse_enter_func enter_func ,
14251451 rb_obj_traverse_leave_func leave_func ,
1426- rb_obj_traverse_final_func final_func )
1452+ rb_obj_traverse_final_func final_func ,
1453+ VALUE * chain ,
1454+ VALUE * exception )
14271455{
14281456 struct obj_traverse_data data = {
14291457 .enter_func = enter_func ,
14301458 .leave_func = leave_func ,
14311459 .rec = NULL ,
1460+ .chain = chain ,
1461+ .exception = exception ,
14321462 };
14331463
1434- if (obj_traverse_i (obj , & data )) return 1 ;
1464+ if (obj_traverse_i (obj , & data )) {
1465+ return 1 ;
1466+ }
14351467 if (final_func && data .rec ) {
14361468 struct rb_obj_traverse_final_data f = {final_func , 0 };
14371469 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1456,14 +1488,45 @@ allow_frozen_shareable_p(VALUE obj)
14561488 return false;
14571489}
14581490
1491+ static VALUE
1492+ try_freeze (VALUE obj )
1493+ {
1494+ rb_funcall (obj , idFreeze , 0 );
1495+ return Qtrue ;
1496+ }
1497+
1498+ struct rescue_freeze_data {
1499+ VALUE exception ;
1500+ };
1501+
1502+ static VALUE
1503+ rescue_freeze (VALUE data , VALUE freeze_exception )
1504+ {
1505+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1506+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("raised calling #freeze" ));
1507+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1508+ rescue_freeze_data -> exception = exception ;
1509+ return Qfalse ;
1510+ }
1511+
14591512static enum obj_traverse_iterator_result
1460- make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result )
1513+ make_shareable_check_shareable_freeze (VALUE obj , enum obj_traverse_iterator_result result , struct obj_traverse_data * data )
14611514{
14621515 if (!RB_OBJ_FROZEN_RAW (obj )) {
1463- rb_funcall (obj , idFreeze , 0 );
1516+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1517+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1518+ if (data -> exception ) {
1519+ * data -> exception = rescue_freeze_data .exception ;
1520+ }
1521+ return traverse_stop ;
1522+ }
14641523
14651524 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1466- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1525+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1526+ if (data -> exception ) {
1527+ * data -> exception = exception ;
1528+ }
1529+ return traverse_stop ;
14671530 }
14681531
14691532 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1477,7 +1540,7 @@ make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_resu
14771540static int obj_refer_only_shareables_p (VALUE obj );
14781541
14791542static enum obj_traverse_iterator_result
1480- make_shareable_check_shareable (VALUE obj )
1543+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
14811544{
14821545 VM_ASSERT (!SPECIAL_CONST_P (obj ));
14831546
@@ -1490,7 +1553,8 @@ make_shareable_check_shareable(VALUE obj)
14901553
14911554 if (type -> flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC ) {
14921555 if (obj_refer_only_shareables_p (obj )) {
1493- make_shareable_check_shareable_freeze (obj , traverse_skip );
1556+ enum obj_traverse_iterator_result result = make_shareable_check_shareable_freeze (obj , traverse_skip , data );
1557+ if (result == traverse_stop ) return traverse_stop ;
14941558 RB_OBJ_SET_SHAREABLE (obj );
14951559 return traverse_skip ;
14961560 }
@@ -1500,11 +1564,19 @@ make_shareable_check_shareable(VALUE obj)
15001564 }
15011565 }
15021566 else if (rb_obj_is_proc (obj )) {
1503- rb_proc_ractor_make_shareable (obj , Qundef );
1567+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
1568+ rb_proc_t * proc = (rb_proc_t * )RTYPEDDATA_DATA (obj );
1569+ if (proc -> block .type != block_type_iseq ) rb_raise (rb_eRuntimeError , "not supported yet" );
1570+
1571+ if (data -> exception ) {
1572+ * data -> exception = rb_exc_new3 (rb_eRactorIsolationError , rb_sprintf ("Proc's self is not shareable: %" PRIsVALUE , obj ));
1573+ }
1574+ return traverse_stop ;
1575+ }
15041576 return traverse_cont ;
15051577 }
15061578 else {
1507- rb_raise ( rb_eRactorError , "can not make shareable object for %+" PRIsVALUE , obj ) ;
1579+ return traverse_stop ;
15081580 }
15091581 }
15101582
@@ -1529,7 +1601,7 @@ make_shareable_check_shareable(VALUE obj)
15291601 break ;
15301602 }
15311603
1532- return make_shareable_check_shareable_freeze (obj , traverse_cont );
1604+ return make_shareable_check_shareable_freeze (obj , traverse_cont , data );
15331605}
15341606
15351607static enum obj_traverse_iterator_result
@@ -1546,9 +1618,20 @@ mark_shareable(VALUE obj)
15461618VALUE
15471619rb_ractor_make_shareable (VALUE obj )
15481620{
1549- rb_obj_traverse (obj ,
1550- make_shareable_check_shareable ,
1551- null_leave , mark_shareable );
1621+ VALUE chain = Qnil ;
1622+ VALUE exception = Qfalse ;
1623+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , & chain , & exception )) {
1624+ if (exception ) {
1625+ VALUE id_mesg = rb_intern ("mesg" );
1626+ VALUE message = rb_attr_get (exception , id_mesg );
1627+ message = rb_sprintf ("%" PRIsVALUE "%" PRIsVALUE , message , chain );
1628+ rb_ivar_set (exception , id_mesg , message );
1629+ rb_exc_raise (exception );
1630+ }
1631+ rb_raise (rb_eRactorError , "can not make shareable object for %+" PRIsVALUE "%" PRIsVALUE , obj , chain );
1632+ }
1633+ RB_GC_GUARD (chain );
1634+ RB_GC_GUARD (exception );
15521635 return obj ;
15531636}
15541637
@@ -1579,7 +1662,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
15791662}
15801663
15811664static enum obj_traverse_iterator_result
1582- shareable_p_enter (VALUE obj )
1665+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
15831666{
15841667 if (RB_OBJ_SHAREABLE_P (obj )) {
15851668 return traverse_skip ;
@@ -1600,11 +1683,9 @@ shareable_p_enter(VALUE obj)
16001683}
16011684
16021685bool
1603- rb_ractor_shareable_p_continue (VALUE obj )
1686+ rb_ractor_shareable_p_continue (VALUE obj , VALUE * chain )
16041687{
1605- if (rb_obj_traverse (obj ,
1606- shareable_p_enter , null_leave ,
1607- mark_shareable )) {
1688+ if (rb_obj_traverse (obj , shareable_p_enter , null_leave , mark_shareable , chain , NULL )) {
16081689 return false;
16091690 }
16101691 else {
@@ -1620,7 +1701,7 @@ rb_ractor_setup_belonging(VALUE obj)
16201701}
16211702
16221703static enum obj_traverse_iterator_result
1623- reset_belonging_enter (VALUE obj )
1704+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
16241705{
16251706 if (rb_ractor_shareable_p (obj )) {
16261707 return traverse_skip ;
@@ -1642,7 +1723,7 @@ static VALUE
16421723ractor_reset_belonging (VALUE obj )
16431724{
16441725#if RACTOR_CHECK_MODE > 0
1645- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL );
1726+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , NULL , NULL );
16461727#endif
16471728 return obj ;
16481729}
0 commit comments