mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-16 09:37:02 +00:00
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>
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
return sel.idx, status
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
`Raw_Queue` is a non-thread-safe queue implementation designed to store messages
|
||||
of fixed size and alignment.
|
||||
|
||||
Reference in New Issue
Block a user