aboutsummaryrefslogtreecommitdiff
path: root/core/sync/chan
diff options
context:
space:
mode:
authorJack Mordaunt <jackmordaunt.dev@gmail.com>2025-06-05 15:38:04 -0300
committerJack Mordaunt <jackmordaunt.dev@gmail.com>2025-06-12 16:14:52 -0300
commitd5b7302ac047dd4c5ee9656d405c84268e27b242 (patch)
tree156c8b53f30274eec77252d6c9e1a441ecb27dd6 /core/sync/chan
parentbe873af003817f7b29ddbd44a4218716d94479dd (diff)
core/sync.try_select_raw: fix TOCTOU
Fixes a TOCTOU where the channel could be used between the call to can_{recv,send} and {recv,send} causing an unexpected blocking operation. To do this we use the non-blocking try_{recv,send} and retry the check in a loop. This guarantees non-blocking select behaviour, at the cost of spinning if the input channels are highly contended. Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat (limited to 'core/sync/chan')
-rw-r--r--core/sync/chan/chan.odin66
1 files changed, 34 insertions, 32 deletions
diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin
index df8dea43b..d20e7d365 100644
--- a/core/sync/chan/chan.odin
+++ b/core/sync/chan/chan.odin
@@ -1189,51 +1189,53 @@ try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs
candidate_count := builtin.len(recvs)+builtin.len(sends)
candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op)))
- count := 0
- for c, i in recvs {
- if can_recv(c) {
- candidates[count] = {
- is_recv = true,
- idx = i,
+ for {
+ count := 0
+
+ for c, i in recvs {
+ if can_recv(c) {
+ candidates[count] = {
+ is_recv = true,
+ idx = i,
+ }
+ count += 1
}
- count += 1
}
- }
- for c, i in sends {
- if can_send(c) {
- candidates[count] = {
- is_recv = false,
- idx = i,
+ for c, i in sends {
+ if can_send(c) {
+ candidates[count] = {
+ is_recv = false,
+ idx = i,
+ }
+ count += 1
}
- count += 1
}
- }
-
- if count == 0 {
- return
- }
- candidate_idx := rand.int_max(count) if count > 0 else 0
-
- sel := candidates[candidate_idx]
- if sel.is_recv {
- status = .Recv
- if !recv_raw(recvs[sel.idx], recv_out) {
+ if count == 0 {
return -1, .None
}
- } else {
- status = .Send
- if !send_raw(sends[sel.idx], send_msgs[sel.idx]) {
- return -1, .None
+
+ candidate_idx := rand.int_max(count) if count > 0 else 0
+
+ sel := candidates[candidate_idx]
+ if sel.is_recv {
+ status = .Recv
+ if !try_recv_raw(recvs[sel.idx], recv_out) {
+ continue
+ }
+ } else {
+ status = .Send
+ if !try_send_raw(sends[sel.idx], send_msgs[sel.idx]) {
+ continue
+ }
}
- }
- return sel.idx, status
+ return sel.idx, status
+ }
}
-
/*
`Raw_Queue` is a non-thread-safe queue implementation designed to store messages
of fixed size and alignment.