feat(channel): select — ready-index wait over multiple receivers
select(&[&dyn Selectable]) -> usize registers (pid, epoch) in every arm under one wait epoch, parks once, and returns the first ready index in documented priority order (BEAM-style; no fairness promise). A closed arm counts as ready — and stays ready forever, so callers drop it from the set once observed. Losing arms need no cancellation pass: the winning wake consumed the epoch, so their registrations die at their wakers' failed CAS or are overwritten by the receiver's next wait on that channel (the single-receiver debug_asserts relax to 'none or own pid' accordingly). The one genuinely new protocol piece is the no-park exit: returning with an arm ready at registration time leaves earlier arms holding LIVE-epoch registrations, whose wakes could fault a later one-shot park as a pending notification. scheduler::retire_wait closes it — bump the epoch (in-flight wakes die at their CAS), eat a notification that already landed (StateWord::clear_notify), then re-observe the stop flag, in that order. Proved by the new loom theorem retire_eats_late_arm_notification; the integration probes in tests/select.rs fire stale loser-arm wakes and assert a subsequent sleep's one-shot park holds its full duration.
This commit is contained in:
@@ -246,6 +246,22 @@ pub(crate) fn begin_wait() -> u32 {
|
||||
with_runtime(|inner| inner.begin_wait(me))
|
||||
}
|
||||
|
||||
/// Close the current actor's wait WITHOUT parking on it. For the no-park
|
||||
/// exit of multi-registration waits (`select` finding an arm ready at
|
||||
/// registration time): bumps the epoch so in-flight wakes die at their CAS,
|
||||
/// eats a notification that already landed, then observes a pending stop —
|
||||
/// in that order. After this, leftover registrations are stale-epoch and
|
||||
/// self-clean at their wakers' failed CAS; nothing can fault the actor's
|
||||
/// next one-shot park.
|
||||
pub(crate) fn retire_wait() {
|
||||
let me = current_pid().expect("retire_wait() called outside an actor");
|
||||
with_runtime(|inner| inner.retire_wait(me));
|
||||
// A request_stop that fired before the clear had its notification
|
||||
// eaten, but it set the stop flag first — observe it here and unwind.
|
||||
// One that fires after re-notifies a Running word as usual.
|
||||
crate::preempt::check_cancelled();
|
||||
}
|
||||
|
||||
/// Request cooperative cancellation of `pid`.
|
||||
///
|
||||
/// Sets the actor's stop flag and wakes it so it observes the flag promptly:
|
||||
|
||||
Reference in New Issue
Block a user