fix(scheduler): entry-side check_cancelled in park_current — stop against a QUEUED actor was lossy
request_stop sets the flag and issues a wildcard unpark, but unpark no-ops on a Queued actor (the pending run "is" the wake). If the actor's first action on resume is a blocking park — no allocation, no check!() on the way — the wake-side-only check in park_current is unreachable: the actor parks with the stop flag already set and no wake is ever coming. The runtime then idles forever. Fix: check_cancelled at the ENTRY of park_current, before the switch. The remaining window (flag lands after the entry check, before the park) is covered by the existing protocol: the stop's unpark finds Running / the prep-to-park window, sets Notified, and the park-return re-queues into the wake-side check. Unwinding from the entry is the same unwind path as the wake side; leftover wait registrations are stale-epoch / dead-generation and self-clean at their wakers' failed CAS. This is bug #1 of the urus chunk-2 session (bug #2, the terminal wake, is the preceding commit). Repro: tests/cancel.rs stop_flagged_while_queued_lands_at_first_park — deadlocks without the fix (watchdog-bounded), passes in <50ms with it.
This commit is contained in:
@@ -212,6 +212,18 @@ pub fn yield_now() {
|
||||
}
|
||||
|
||||
pub fn park_current() {
|
||||
// Entry-side observation point: a stop flagged while we were QUEUED is
|
||||
// otherwise lost — the stop's wildcard unpark no-ops on a Queued actor
|
||||
// (the pending run "is" the wake), so if our first action on resume is
|
||||
// this park, no wake is ever coming and the wake-side check below is
|
||||
// unreachable. Checking here closes that hole; a flag that lands after
|
||||
// this check is covered by the existing protocol (the stop's unpark
|
||||
// finds Running / the prep-to-park window, sets Notified, and the
|
||||
// park-return re-queues us into the wake-side check). Unwinding from
|
||||
// here is the same unwind path as the wake side: leftover wait
|
||||
// registrations are stale-epoch / dead-generation and self-clean at
|
||||
// their wakers' failed CAS.
|
||||
crate::preempt::check_cancelled();
|
||||
runtime::set_yield_intent(YieldIntent::Park);
|
||||
unsafe { crate::context::switch_to_scheduler() };
|
||||
// Observation point on the wakeup side of every blocking primitive
|
||||
|
||||
Reference in New Issue
Block a user