Skip to content

Commit 25cd645

Browse files
committed
WIP Ractor.make_shareable handles freeze failures
1 parent b8abd97 commit 25cd645

2 files changed

Lines changed: 71 additions & 14 deletions

File tree

bootstraptest/test_ractor.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,19 @@ def inspect = "#<Proc>"
922922
end
923923
}
924924

925+
assert_equal '[true, true]', %q{
926+
class Foo
927+
undef_method :freeze
928+
end
929+
930+
begin
931+
Ractor.make_shareable Foo.new
932+
rescue Ractor::Error
933+
cause = $!.cause
934+
[cause.class == NoMethodError, cause.name == :freeze]
935+
end
936+
}
937+
925938
assert_equal '["instance-variable", "instance-variable", nil]', %q{
926939
class C
927940
@iv1 = ""

ractor.c

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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);
11291130
typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
11301131
typedef 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

11431145
struct 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+
13831411
static 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
14471484
rb_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

14851529
static 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

15261570
static 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
15461590
ractor_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

Comments
 (0)