Skip to content

Commit 3aa4a77

Browse files
committed
add while loop around condvar wait
1 parent ce54735 commit 3aa4a77

File tree

4 files changed

+36
-47
lines changed

4 files changed

+36
-47
lines changed

bootstraptest/test_ractor.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ def test n
428428
end
429429
end
430430
431-
10_000.times do |i|
431+
1_000.times do |i|
432432
echo_ractor << i
433433
raise unless port.receive == i
434434
end
@@ -452,7 +452,7 @@ def test n
452452
end
453453
end
454454
455-
10_000.times do |i|
455+
1_000.times do |i|
456456
echo_ractor << i
457457
raise unless port.receive == i
458458
end
@@ -2002,6 +2002,7 @@ def initialize(a)
20022002
r = Ractor.new port do |port|
20032003
loop do
20042004
obj = Ractor.receive
2005+
break if obj == :exit
20052006
val = case obj
20062007
when Hash
20072008
obj['key'] == 'value' && obj.instance_variable_get("@b") == 'b'
@@ -2049,6 +2050,9 @@ def initialize(a)
20492050
end
20502051
end
20512052
end
2053+
2054+
r << :exit
2055+
r.join
20522056
'ok'
20532057
}
20542058

ractor_sync.c

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ basket_type_name(enum ractor_basket_type type)
893893

894894
//
895895

896-
#else // win32
896+
#else // win32 + wasm
897897

898898
static void
899899
ractor_cond_wait(rb_ractor_t *r, rb_thread_t *th)
@@ -903,7 +903,10 @@ ractor_cond_wait(rb_ractor_t *r, rb_thread_t *th)
903903
VM_ASSERT(locked_by && locked_by != Qnil);
904904
r->sync.locked_by = Qnil;
905905
#endif
906-
rb_native_cond_wait(&th->sched.ractor_action_wakeup_cond, &r->sync.lock);
906+
th->status = THREAD_STOPPED_FOREVER;
907+
while (th->status == THREAD_STOPPED_FOREVER) {
908+
rb_native_cond_wait(&th->sched.ractor_action_wakeup_cond, &r->sync.lock);
909+
}
907910

908911
#if RACTOR_CHECK_MODE > 0
909912
r->sync.locked_by = locked_by;
@@ -943,15 +946,14 @@ rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_fun
943946
static void
944947
rb_ractor_sched_wakeup(rb_ractor_t *r, rb_thread_t *th)
945948
{
946-
RACTOR_LOCK(r);
947-
{
949+
if (th->status == THREAD_STOPPED_FOREVER) {
950+
th->status = THREAD_RUNNABLE;
948951
rb_native_cond_signal(&th->sched.ractor_action_wakeup_cond);
949952
}
950-
RACTOR_UNLOCK(r);
951953
}
952954
#endif
953955

954-
// Wake up all ractors that are currently blocked on `rp`
956+
// TODO: wake up all threads on r that are currently blocked on `rp`, not all threads on r
955957
static void
956958
ractor_wakeup_all(const struct ractor_port *rp, enum ractor_wakeup_status wakeup_status)
957959
{
@@ -960,40 +962,21 @@ ractor_wakeup_all(const struct ractor_port *rp, enum ractor_wakeup_status wakeup
960962

961963
RUBY_DEBUG_LOG("r:%u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
962964

963-
struct ractor_waiter *waiter;
964-
struct ractor_waiter **waiters_on_port = NULL;
965-
int num_waiters = 0;
966965
RACTOR_LOCK(r);
967-
{
968-
if (!ccan_list_empty(&r->sync.waiters)) {
969-
ccan_list_for_each(&r->sync.waiters, waiter, node) {
970-
if (waiter->wakeup_status == wakeup_none && (!waiter->port || ractor_port_id(waiter->port) == ractor_port_id(rp))) {
971-
num_waiters++;
972-
}
973-
}
974-
if (num_waiters > 0) {
975-
waiters_on_port = ALLOC_N(struct ractor_waiter*, num_waiters);
976-
int i = 0;
977-
ccan_list_for_each(&r->sync.waiters, waiter, node) {
978-
if (waiter->wakeup_status == wakeup_none && (!waiter->port || ractor_port_id(waiter->port) == ractor_port_id(rp))) {
979-
waiter->wakeup_status = wakeup_status;
980-
waiters_on_port[i++] = waiter;
981-
}
982-
}
983-
for (int i = 0; i < num_waiters; i++) {
984-
ccan_list_del(&waiters_on_port[i]->node);
985-
}
986-
}
966+
while (1) {
967+
struct ractor_waiter *waiter = ccan_list_pop(&r->sync.waiters, struct ractor_waiter, node);
968+
969+
if (waiter) {
970+
VM_ASSERT(waiter->wakeup_status == wakeup_none);
971+
972+
waiter->wakeup_status = wakeup_status;
973+
rb_ractor_sched_wakeup(r, waiter->th);
974+
}
975+
else {
976+
break;
987977
}
988978
}
989979
RACTOR_UNLOCK(r);
990-
991-
for (int i = 0; i < num_waiters; i++) {
992-
waiter = waiters_on_port[i];
993-
RUBY_ASSERT(!waiter->port || ractor_port_id(waiter->port) == ractor_port_id(rp));
994-
rb_ractor_sched_wakeup(r, waiter->th);
995-
}
996-
if (waiters_on_port) ruby_xfree(waiters_on_port);
997980
}
998981

999982
static void
@@ -1010,22 +993,18 @@ ubf_ractor_wait(void *ptr)
1010993

1011994
rb_native_mutex_unlock(&th->interrupt_lock);
1012995
{
1013-
bool should_wake = false;
1014996
RACTOR_LOCK(r);
1015997
{
1016998
if (waiter->wakeup_status == wakeup_none) {
1017999
RUBY_DEBUG_LOG("waiter:%p", (void *)waiter);
10181000

10191001
waiter->wakeup_status = wakeup_by_interrupt;
10201002
ccan_list_del(&waiter->node);
1021-
should_wake = true;
1003+
rb_ractor_sched_wakeup(r, th);
10221004
}
10231005
}
10241006
RACTOR_UNLOCK(r);
10251007

1026-
if (should_wake) {
1027-
rb_ractor_sched_wakeup(r, th);
1028-
}
10291008
}
10301009
rb_native_mutex_lock(&th->interrupt_lock);
10311010
}
@@ -1104,7 +1083,7 @@ ractor_check_received(rb_ractor_t *cr, struct ractor_queue *messages)
11041083
return received;
11051084
}
11061085

1107-
// The port can be NULL when you're waiting on any ractor to wake you up, such as with `Ractor.select(*ractors)`
1086+
// The port `rp` can be NULL when you're waiting on multiple ractors to wake you up, such as with `Ractor.select(*ractors)`
11081087
static void
11091088
ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
11101089
{
@@ -1114,7 +1093,7 @@ ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ra
11141093
RACTOR_LOCK_SELF(cr);
11151094
{
11161095
if (ractor_check_received(cr, &messages)) {
1117-
deliverred = true;
1096+
deliverred = true; // any message was delivered, not necessarily to this port
11181097
}
11191098
else {
11201099
ractor_wait(ec, cr, rp);
@@ -1432,7 +1411,7 @@ ractor_selector__wait(rb_execution_context_t *ec, VALUE selector)
14321411
return rb_ary_new_from_args(2, data.rpv, data.v);
14331412
}
14341413

1435-
ractor_wait_receive(ec, cr, NULL); // wait on any ractor port
1414+
ractor_wait_receive(ec, cr, NULL); // wait on any ractor port. TODO: should only wait on a group of ports
14361415
}
14371416
}
14381417

test/ruby/test_ractor.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,15 @@ def ractor_job(job_count, array_size)
154154
w_idx = i % 4
155155
workers[w_idx].send(job)
156156
end
157+
workers.each do |w|
158+
w.send(nil)
159+
end
157160
results = []
158161
jobs.size.times do
159162
result = port.receive # dnt receive
160163
results << result
161164
end
165+
workers.each(&:join)
162166
results
163167
end
164168
threads = []

thread_pthread.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1369,9 +1369,11 @@ rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_fun
13691369
thread_sched_wait_running_turn(sched, th, can_direct_transfer);
13701370
th->status = THREAD_RUNNABLE;
13711371
}
1372-
rb_ractor_lock_self(cr);
1372+
// NOTE: locking ractor here can result in deadlock (lock inversion issue).
1373+
// We must lock ractor after unlocking thread_sched_lock
13731374
}
13741375
thread_sched_unlock(sched, th);
1376+
rb_ractor_lock_self(cr);
13751377

13761378
ubf_clear(th);
13771379

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy