Skip to main content

InterruptibleEvent

Struct InterruptibleEvent 

Source
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:

  1. The given deadline expires.
  2. At least one of the notify or interrupt functions 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

Source

pub fn new() -> Arc<Self> ⓘ

Source

pub fn get_owner(&self) -> Option<Koid>

Returns the owner of the active underlying futex, if any.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for InterruptibleEvent

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T, D> Encode<Ambiguous1, D> for T
where D: ResourceDialect,

§

unsafe fn encode( self, _encoder: &mut Encoder<'_, D>, _offset: usize, _depth: Depth, ) -> Result<(), Error>

Encodes the object into the encoder’s buffers. Any handles stored in the object are swapped for Handle::INVALID. Read more
§

impl<T, D> Encode<Ambiguous2, D> for T
where D: ResourceDialect,

§

unsafe fn encode( self, _encoder: &mut Encoder<'_, D>, _offset: usize, _depth: Depth, ) -> Result<(), Error>

Encodes the object into the encoder’s buffers. Any handles stored in the object are swapped for Handle::INVALID. Read more
§

impl<F, N> FidlIntoNative<Box<N>> for F
where F: FidlIntoNative<N>,

§

fn fidl_into_native(self) -> Box<N>

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.