@@ -1125,7 +1125,8 @@ enum obj_traverse_iterator_result {
11251125 traverse_stop ,
11261126};
11271127
1128- typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj , VALUE chain );
1128+ struct obj_traverse_data ;
1129+ typedef enum obj_traverse_iterator_result (* rb_obj_traverse_enter_func )(VALUE obj , struct obj_traverse_data * data );
11291130typedef enum obj_traverse_iterator_result (* rb_obj_traverse_leave_func )(VALUE obj );
11301131typedef enum obj_traverse_iterator_result (* rb_obj_traverse_final_func )(VALUE obj );
11311132
@@ -1137,7 +1138,8 @@ struct obj_traverse_data {
11371138
11381139 st_table * rec ;
11391140 VALUE rec_hash ; // objects seen during traversal
1140- VALUE chain ; // chain of objects that led to currently traversed object, for error reporting
1141+ VALUE chain ; // chain of objects built in the failure case
1142+ VALUE exception ; // exception raised trying to freeze an object
11411143};
11421144
11431145struct obj_traverse_callback_data {
@@ -1206,7 +1208,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
12061208{
12071209 if (RB_SPECIAL_CONST_P (obj )) return 0 ;
12081210
1209- switch (data -> enter_func (obj , data -> chain )) {
1211+ switch (data -> enter_func (obj , data )) {
12101212 case traverse_cont : break ;
12111213 case traverse_skip : return 0 ; // skip children
12121214 case traverse_stop : return 1 ; // stop search
@@ -1346,16 +1348,21 @@ rb_obj_traverse(VALUE obj,
13461348 rb_obj_traverse_enter_func enter_func ,
13471349 rb_obj_traverse_leave_func leave_func ,
13481350 rb_obj_traverse_final_func final_func ,
1349- VALUE chain )
1351+ VALUE chain ,
1352+ VALUE * exception )
13501353{
13511354 struct obj_traverse_data data = {
13521355 .enter_func = enter_func ,
13531356 .leave_func = leave_func ,
13541357 .rec = NULL ,
13551358 .chain = chain ,
1359+ .exception = Qfalse ,
13561360 };
13571361
1358- if (obj_traverse_i (obj , & data )) return 1 ;
1362+ if (obj_traverse_i (obj , & data )) {
1363+ if (exception && data .exception ) * exception = data .exception ;
1364+ return 1 ;
1365+ }
13591366 if (final_func && data .rec ) {
13601367 struct rb_obj_traverse_final_data f = {final_func , 0 };
13611368 st_foreach (data .rec , obj_traverse_final_i , (st_data_t )& f );
@@ -1380,8 +1387,29 @@ allow_frozen_shareable_p(VALUE obj)
13801387 return false;
13811388}
13821389
1390+ static VALUE
1391+ try_freeze (VALUE obj )
1392+ {
1393+ rb_funcall (obj , idFreeze , 0 );
1394+ return Qtrue ;
1395+ }
1396+
1397+ struct rescue_freeze_data {
1398+ VALUE exception ;
1399+ };
1400+
1401+ static VALUE
1402+ rescue_freeze (VALUE data , VALUE freeze_exception )
1403+ {
1404+ struct rescue_freeze_data * rescue_freeze_data = (struct rescue_freeze_data * )data ;
1405+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("failed to call #freeze" ));
1406+ rb_ivar_set (exception , rb_intern ("cause" ), freeze_exception );
1407+ rescue_freeze_data -> exception = exception ;
1408+ return Qfalse ;
1409+ }
1410+
13831411static enum obj_traverse_iterator_result
1384- make_shareable_check_shareable (VALUE obj , VALUE chain )
1412+ make_shareable_check_shareable (VALUE obj , struct obj_traverse_data * data )
13851413{
13861414 VM_ASSERT (!SPECIAL_CONST_P (obj ));
13871415
@@ -1390,7 +1418,7 @@ make_shareable_check_shareable(VALUE obj, VALUE chain)
13901418 }
13911419 else if (!allow_frozen_shareable_p (obj )) {
13921420 if (rb_obj_is_proc (obj )) {
1393- if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , chain )) {
1421+ if (!rb_proc_ractor_make_shareable_continue (obj , Qundef , data -> chain )) {
13941422 return traverse_stop ;
13951423 }
13961424 return traverse_cont ;
@@ -1422,10 +1450,19 @@ make_shareable_check_shareable(VALUE obj, VALUE chain)
14221450 }
14231451
14241452 if (!RB_OBJ_FROZEN_RAW (obj )) {
1425- rb_funcall (obj , idFreeze , 0 );
1453+ struct rescue_freeze_data rescue_freeze_data = { 0 };
1454+ if (!rb_rescue (try_freeze , obj , rescue_freeze , (VALUE )& rescue_freeze_data )) {
1455+ data -> exception = rescue_freeze_data .exception ;
1456+ return traverse_stop ;
1457+ }
14261458
14271459 if (UNLIKELY (!RB_OBJ_FROZEN_RAW (obj ))) {
1428- rb_raise (rb_eRactorError , "#freeze does not freeze object correctly" );
1460+ VALUE exception = rb_exc_new3 (rb_eRactorError , rb_str_new_cstr ("#freeze does not freeze object correctly" ));
1461+ if (rescue_freeze_data .exception ) {
1462+ rb_ivar_set (exception , rb_intern ("cause" ), rescue_freeze_data .exception );
1463+ }
1464+ data -> exception = exception ;
1465+ return traverse_stop ;
14291466 }
14301467
14311468 if (RB_OBJ_SHAREABLE_P (obj )) {
@@ -1447,7 +1484,14 @@ VALUE
14471484rb_ractor_make_shareable (VALUE obj )
14481485{
14491486 VALUE chain = rb_ary_new ();
1450- if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , chain )) {
1487+ VALUE exception = Qnil ;
1488+ if (rb_obj_traverse (obj , make_shareable_check_shareable , null_leave , mark_shareable , chain , & exception )) {
1489+ if (exception != Qnil ) {
1490+ VALUE message = rb_check_funcall (exception , rb_intern ("message" ), 0 , 0 );
1491+ exception = rb_funcall (exception , rb_intern ("exception" ), 1 , rb_sprintf ("%s\n"
1492+ "Reference chain: %" PRIsVALUE , RSTRING_PTR (message ), chain ));
1493+ rb_exc_raise (exception );
1494+ }
14511495 rb_raise (rb_eRactorError ,
14521496 "can not make shareable object for %+" PRIsVALUE "\n"
14531497 "Reference chain: %" PRIsVALUE ,
@@ -1483,7 +1527,7 @@ rb_ractor_ensure_main_ractor(const char *msg)
14831527}
14841528
14851529static enum obj_traverse_iterator_result
1486- shareable_p_enter (VALUE obj , VALUE chain )
1530+ shareable_p_enter (VALUE obj , struct obj_traverse_data * data )
14871531{
14881532 if (RB_OBJ_SHAREABLE_P (obj )) {
14891533 return traverse_skip ;
@@ -1508,7 +1552,7 @@ rb_ractor_shareable_p_continue(VALUE obj, VALUE chain)
15081552{
15091553 if (rb_obj_traverse (obj ,
15101554 shareable_p_enter , null_leave ,
1511- mark_shareable , chain )) {
1555+ mark_shareable , chain , NULL )) {
15121556 return false;
15131557 }
15141558 else {
@@ -1524,7 +1568,7 @@ rb_ractor_setup_belonging(VALUE obj)
15241568}
15251569
15261570static enum obj_traverse_iterator_result
1527- reset_belonging_enter (VALUE obj , VALUE chain )
1571+ reset_belonging_enter (VALUE obj , struct obj_traverse_data * data )
15281572{
15291573 if (rb_ractor_shareable_p (obj )) {
15301574 return traverse_skip ;
@@ -1546,7 +1590,7 @@ static VALUE
15461590ractor_reset_belonging (VALUE obj )
15471591{
15481592#if RACTOR_CHECK_MODE > 0
1549- rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , Qfalse );
1593+ rb_obj_traverse (obj , reset_belonging_enter , null_leave , NULL , Qfalse , NULL );
15501594#endif
15511595 return obj ;
15521596}
0 commit comments