pub struct InterruptibleEvent { /* private fields */ }Expand description
A blocking object that can either be notified normally or interrupted.
To block using an InterruptibleEvent, first call Self::begin_wait (or
Self::begin_wait_requeueable if the wait should allow dynamic PI owner assignment). At this
point, the event is in a waiting state, and future calls to Self::notify or
Self::interrupt will terminate the wait.
After begin_wait (or begin_wait_requeueable) returns, call EventWaitGuard::block_until
on the returned guard to block the current thread until one of the following conditions occurs:
- The given deadline expires.
- At least one of the
notifyorinterruptfunctions was called after the wait began.
Alternatively, dropping the EventWaitGuard without calling block_until cancels the wait
and resets the event back to the ready state.
It’s safe to call notify or interrupt at any time. However, only one wait cycle can be
active at a time: begin_wait or begin_wait_requeueable cannot be called again until the
EventWaitGuard it returns is consumed by block_until or dropped. When an
InterruptibleEvent is reused across multiple wait cycles, notify, interrupt, and
Self::assign_new_owner are not bound to a specific cycle token; see Self::begin_wait and
Self::assign_new_owner for cross-cycle concurrency considerations.
InterruptibleEvent uses two-phase waiting so that clients can register for notification,
perform some related work, and then start blocking. This approach ensures that clients do not
miss notifications that arrive after they perform the related work but before they actually
start blocking.
§Priority Inheritance and Dynamic Futex Owner Assignment
InterruptibleEvent supports Zircon Priority Inheritance (PI). If the target owner thread is
known at wait time, initiate the wait with Self::begin_wait and pass the owner thread
directly to block_until / zx_futex_wait.
When the owner thread is not known when waiting begins (for example, when a transaction is
queued to a process-wide worker pool), initiate the wait with Self::begin_wait_requeueable;
Self::assign_new_owner can then be called by the worker thread that later dequeues the work
item. assign_new_owner uses zx_futex_requeue to atomically transfer waiting threads to a
secondary requeue_target futex while designating the worker thread as the futex PI owner.
Waits started with Self::begin_wait reject assign_new_owner so a known PI owner cannot be
overridden.
TODO(https://fxbug.dev/542307988): The long-term solution is a dedicated Zircon syscall (such as
zx_futex_assign_owner) to assign or update the PI owner of an existing futex without
requiring a secondary requeue target futex.
Implementations§
Source§impl InterruptibleEvent
impl InterruptibleEvent
pub fn new() -> Arc<Self> ⓘ
Sourcepub fn get_owner(&self) -> Option<Koid>
pub fn get_owner(&self) -> Option<Koid>
Returns the owner of the active underlying futex, if any.
Sourcepub fn begin_wait(&self) -> EventWaitGuard<'_>
pub fn begin_wait(&self) -> EventWaitGuard<'_>
Called to initiate a wait cycle that does not allow dynamic PI owner assignment via
Self::assign_new_owner.
Calls to Self::notify or Self::interrupt after this function returns, and before the
returned EventWaitGuard is dropped, will cause the event to wake up. Calls that complete
while the event is idle (before begin_wait or after the previous EventWaitGuard has been
dropped) are ignored.
Calls to Self::assign_new_owner during a wait started with begin_wait fail with
Err(zx::Status::BAD_STATE), preserving any PI owner passed directly to
EventWaitGuard::block_until. Use Self::begin_wait_requeueable when dynamic PI owner
assignment is needed.
When reusing an InterruptibleEvent across multiple wait cycles, notify and interrupt
are not tied to a specific wait cycle: an in-flight call intended for cycle N that races
with cycle N’s guard drop and cycle N + 1’s begin_wait can apply to cycle N + 1.
Callers reusing an event must tolerate spurious wakeups from a late notify (or late
interrupt).
Once called, neither begin_wait nor begin_wait_requeueable can be called again until the
returned EventWaitGuard is consumed by EventWaitGuard::block_until or dropped.
§Panics
Panics if a previous EventWaitGuard for this event has not yet been dropped.
Sourcepub fn begin_wait_requeueable(&self) -> EventWaitGuard<'_>
pub fn begin_wait_requeueable(&self) -> EventWaitGuard<'_>
Called to initiate a wait cycle that allows dynamic PI owner assignment via
Self::assign_new_owner.
Behaves like Self::begin_wait, except Self::assign_new_owner can dynamically requeue
the wait to assign a PI owner once during this wait cycle. Requeue eligibility is cleared
automatically when the returned EventWaitGuard is dropped (or once assign_new_owner,
notify, or interrupt transitions the event out of WAITING_REQUEUEABLE).
§Panics
Panics if a previous EventWaitGuard for this event has not yet been dropped.
Sourcepub fn assign_new_owner(&self, new_owner: &Arc<Thread>) -> Result<(), Status>
pub fn assign_new_owner(&self, new_owner: &Arc<Thread>) -> Result<(), Status>
Assigns new_owner as the Priority Inheritance (PI) owner for waiting thread(s).
If the event is in a requeueable wait (started via Self::begin_wait_requeueable), this
dynamically requeues any threads waiting on futex to requeue_target, designating
new_owner as the futex PI owner.
This establishes Zircon Priority Inheritance (PI) from the waiting thread(s) to
new_owner when the owner was not known when waiting began.
Note: This can only succeed once per Self::begin_wait_requeueable cycle (while futex
is WAITING_REQUEUEABLE). Waits started with Self::begin_wait, idle events, and events
that have already been requeued or woken return Err(zx::Status::BAD_STATE).
§Event Reuse and Cycle Turnover
assign_new_owner is not bound to a specific wait-cycle token. Waits started with
Self::begin_wait use WAITING rather than WAITING_REQUEUEABLE, so an in-flight
assign_new_owner from an earlier cycle cannot requeue them or override a known new_owner
passed to EventWaitGuard::block_until. However, if an InterruptibleEvent is reused
across consecutive Self::begin_wait_requeueable cycles, an in-flight assign_new_owner
from cycle N that races with cycle N’s EventWaitGuard drop and cycle N + 1’s
begin_wait_requeueable can still succeed against cycle N + 1.
TODO(https://fxbug.dev/542307988): Replace this requeue pattern with a dedicated Zircon syscall to assign a new owner to a futex with waiting threads once available.
Sourcepub fn is_waiting(&self) -> bool
pub fn is_waiting(&self) -> bool
Returns whether begin_wait or begin_wait_requeueable has been called and the event has
not yet been notified, interrupted, or reset.
Sourcepub fn notify(&self)
pub fn notify(&self)
Wake up the event normally.
If this function completes while no wait cycle is active (before begin_wait /
begin_wait_requeueable or after the EventWaitGuard is dropped), this notification is
ignored; when an event is reused, an in-flight call racing with cycle turnover may wake the
next wait cycle. Calling this function repeatedly within a cycle has no additional effect.
If both notify and interrupt are called, the state observed by block_until is a race.
Sourcepub fn interrupt(&self)
pub fn interrupt(&self)
Wake up the event because of an interruption.
If this function completes while no wait cycle is active (before begin_wait /
begin_wait_requeueable or after the EventWaitGuard is dropped), this interruption is
ignored; when an event is reused, an in-flight call racing with cycle turnover may interrupt
the next wait cycle. Calling this function repeatedly within a cycle has no additional
effect. If both notify and interrupt are called, the state observed by block_until is
a race.
Trait Implementations§
Source§impl Debug for InterruptibleEvent
impl Debug for InterruptibleEvent
Auto Trait Implementations§
impl !Freeze for InterruptibleEvent
impl !RefUnwindSafe for InterruptibleEvent
impl Send for InterruptibleEvent
impl Sync for InterruptibleEvent
impl Unpin for InterruptibleEvent
impl UnsafeUnpin for InterruptibleEvent
impl UnwindSafe for InterruptibleEvent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T, D> Encode<Ambiguous1, D> for Twhere
D: ResourceDialect,
impl<T, D> Encode<Ambiguous1, D> for Twhere
D: ResourceDialect,
§impl<T, D> Encode<Ambiguous2, D> for Twhere
D: ResourceDialect,
impl<T, D> Encode<Ambiguous2, D> for Twhere
D: ResourceDialect,
§impl<F, N> FidlIntoNative<Box<N>> for Fwhere
F: FidlIntoNative<N>,
impl<F, N> FidlIntoNative<Box<N>> for Fwhere
F: FidlIntoNative<N>,
fn fidl_into_native(self) -> Box<N>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more