Skip to content

Commit fff1edf

Browse files
committed
fix Ractor.yield(obj, move: true)
Ractor.yield(obj, move: true) and Ractor.select(..., yield_value: obj, move: true) tried to yield a value with move semantices, but if the trial is faild, the obj should not become a moved object. To keep this rule, `wait_moving` wait status is introduced. New yield/take process: (1) If a ractor tried to yield (move:true), make taking racotr's wait status `wait_moving` and make a moved object by `ractor_move(obj)` and wakeup taking ractor. (2) If a ractor tried to take a message from a ractor waiting fo yielding (move:true), wakeup the ractor and wait for (1).
1 parent d0d6227 commit fff1edf

File tree

3 files changed

+84
-15
lines changed

3 files changed

+84
-15
lines changed

bootstraptest/test_ractor.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,31 @@ def check obj1
709709
end
710710
}
711711

712+
# yield/move should not make moved object when the yield is not succeeded
713+
assert_equal '"str"', %q{
714+
R = Ractor.new{}
715+
M = Ractor.current
716+
r = Ractor.new do
717+
s = 'str'
718+
selected_r, v = Ractor.select R, yield_value: s, move: true
719+
raise if selected_r != R # taken from R
720+
M.send s.inspect # s should not be a moved object
721+
end
722+
723+
Ractor.receive
724+
}
725+
726+
# yield/move can fail
727+
assert_equal "allocator undefined for Thread", %q{
728+
r = Ractor.new do
729+
obj = Thread.new{}
730+
Ractor.yield obj
731+
rescue => e
732+
e.message
733+
end
734+
r.take
735+
}
736+
712737
# Access to global-variables are prohibited
713738
assert_equal 'can not access global variables $gv from non-main Ractors', %q{
714739
$gv = 1

ractor.c

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ static VALUE ractor_move(VALUE obj); // in this file
916916
static VALUE ractor_copy(VALUE obj); // in this file
917917

918918
static void
919-
ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will)
919+
ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will, bool is_yield)
920920
{
921921
basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
922922
basket->exception = exc;
@@ -935,15 +935,21 @@ ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket,
935935
}
936936
else {
937937
basket->type = basket_type_move;
938-
basket->v = ractor_move(obj);
938+
939+
if (is_yield) {
940+
basket->v = obj; // call ractor_move() when yielding timing.
941+
}
942+
else {
943+
basket->v = ractor_move(obj);
944+
}
939945
}
940946
}
941947

942948
static VALUE
943949
ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
944950
{
945951
struct rb_ractor_basket basket;
946-
ractor_basket_setup(ec, &basket, obj, move, false, false);
952+
ractor_basket_setup(ec, &basket, obj, move, false, false, false);
947953
ractor_send_basket(ec, r, &basket);
948954
return r->pub.self;
949955
}
@@ -958,17 +964,23 @@ ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
958964

959965
RACTOR_LOCK(r);
960966
{
961-
if (ractor_wakeup(r, wait_yielding, wakeup_by_take)) {
967+
if (ractor_sleeping_by(r, wait_yielding)) {
968+
MAYBE_UNUSED(bool) wakeup_result;
962969
VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
963-
basket = r->sync.wait.yielded_basket;
964-
ractor_basket_clear(&r->sync.wait.yielded_basket);
970+
971+
if (r->sync.wait.yielded_basket.type == basket_type_move) {
972+
wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
973+
}
974+
else {
975+
wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
976+
basket = r->sync.wait.yielded_basket;
977+
ractor_basket_clear(&r->sync.wait.yielded_basket);
978+
}
979+
VM_ASSERT(wakeup_result);
965980
}
966981
else if (r->sync.outgoing_port_closed) {
967982
closed = true;
968983
}
969-
else {
970-
// not reached.
971-
}
972984
}
973985
RACTOR_UNLOCK(r);
974986

@@ -985,6 +997,12 @@ ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
985997
}
986998
}
987999

1000+
static VALUE
1001+
ractor_yield_move_body(VALUE v)
1002+
{
1003+
return ractor_move(v);
1004+
}
1005+
9881006
static bool
9891007
ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_basket *basket)
9901008
{
@@ -1009,8 +1027,34 @@ ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_b
10091027

10101028
RACTOR_LOCK(r);
10111029
{
1012-
if (ractor_wakeup(r, wait_taking, wakeup_by_yield)) {
1030+
if (ractor_sleeping_by(r, wait_taking)) {
10131031
VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
1032+
1033+
if (basket->type == basket_type_move) {
1034+
enum ractor_wait_status prev_wait_status = r->sync.wait.status;
1035+
r->sync.wait.status = wait_moving;
1036+
1037+
RACTOR_UNLOCK(r);
1038+
{
1039+
int state;
1040+
VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
1041+
if (state) {
1042+
r->sync.wait.status = prev_wait_status;
1043+
rb_jump_tag(state);
1044+
}
1045+
else {
1046+
basket->v = moved_value;
1047+
}
1048+
}
1049+
RACTOR_LOCK(r);
1050+
1051+
if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1052+
// terminating?
1053+
}
1054+
}
1055+
else {
1056+
ractor_wakeup(r, wait_taking, wakeup_by_yield);
1057+
}
10141058
r->sync.wait.taken_basket = *basket;
10151059
}
10161060
else {
@@ -1080,16 +1124,15 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL
10801124
}
10811125
rs = NULL;
10821126

1127+
restart:
1128+
10831129
if (yield_p) {
10841130
actions[rs_len].type = ractor_select_action_yield;
10851131
actions[rs_len].v = Qundef;
10861132
wait_status |= wait_yielding;
1087-
1088-
ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false);
1133+
ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false, true);
10891134
}
10901135

1091-
restart:
1092-
10931136
// TODO: shuffle actions
10941137

10951138
while (1) {
@@ -1580,7 +1623,7 @@ ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool e
15801623
ASSERT_ractor_unlocking(cr);
15811624

15821625
struct rb_ractor_basket basket;
1583-
ractor_basket_setup(ec, &basket, v, Qfalse, exc, true);
1626+
ractor_basket_setup(ec, &basket, v, Qfalse, exc, true, true /* this flag is ignored because move is Qfalse */);
15841627

15851628
retry:
15861629
if (ractor_try_yield(ec, cr, &basket)) {

ractor_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct rb_ractor_sync {
6161
wait_receiving = 0x01,
6262
wait_taking = 0x02,
6363
wait_yielding = 0x04,
64+
wait_moving = 0x08,
6465
} status;
6566

6667
enum ractor_wakeup_status {

0 commit comments

Comments
 (0)