Skip to main content

ThreadpoolWait

Struct ThreadpoolWait 

Source
pub struct ThreadpoolWait { /* private fields */ }
Expand description

An owned thread-pool wait object bound to one waitable handle.

The object owns the handle, so the handle cannot be closed while a wait is pending. A newly created wait is idle; arm it with ThreadpoolWait::arm, and rearm from inside the callback with WaitActivation::rearm.

Drop disarms before draining callbacks, then closes the object and only afterwards releases the callback context and the handle.

Unlike ThreadpoolTimer, the callback can run concurrently with itself: a wait’s re-arm takes effect immediately, so re-arming while the handle is still signalled queues the next activation before the current callback returns. See WaitActivation::rearm for the measurements and the two ways to avoid it.

§Examples

Watch an event once. The wait takes ownership of the handle, and ThreadpoolWait::handle borrows it back for signalling:

use std::os::windows::io::AsRawHandle;
use std::sync::mpsc;
use windows_sys::Win32::System::Threading::SetEvent;
use windows_threadpool_sys::wait::{ThreadpoolWait, WaitResult, WaitableHandle};

let event = WaitableHandle::event(true, false)?;

let (tx, rx) = mpsc::channel();
let sender = std::sync::Mutex::new(tx);
let wait = ThreadpoolWait::new(event, move |activation| {
    let _ = sender.lock().expect("send").send(activation.result());
}, None)?;

wait.arm(None);
// SAFETY: the wait owns the event, so the handle is still open.
unsafe { SetEvent(wait.handle().as_raw_handle()) };

assert_eq!(rx.recv().expect("activation"), WaitResult::Signalled);

Keep watching across activations by rearming from inside the callback, which is what the SDK requires – an activation consumes the arming:

use windows_threadpool_sys::wait::{ThreadpoolWait, WaitableHandle};

let event = WaitableHandle::event(false, false)?;

let seen = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&seen);
let wait = ThreadpoolWait::new(event, move |activation| {
    counter.fetch_add(1, Ordering::SeqCst);
    activation.rearm(None);
}, None)?;

wait.arm(None);
for _ in 0..3 {
    // SAFETY: the wait owns the event, so the handle is still open.
    unsafe { SetEvent(wait.handle().as_raw_handle()) };
    std::thread::sleep(std::time::Duration::from_millis(5));
}

wait.disarm();
wait.wait();
assert!(seen.load(Ordering::SeqCst) >= 1);

Implementations§

Source§

impl ThreadpoolWait

Source

pub fn new<F>( handle: WaitableHandle, callback: F, env: Option<&mut CallbackEnviron<'_>>, ) -> Result<Self>
where F: Fn(&WaitActivation<'_>) + Send + Sync + 'static,

Create an idle wait watching handle.

The object takes ownership of the handle and closes it on drop, which is what guarantees the handle outlives any pending wait.

Pass Some(env) to select a private pool or callback priority; None uses the process-default pool with default priority.

The callback runs on a shared, process-managed pool thread, must restore any thread state it changes, and must not terminate its thread. It must not panic: a panic unwinds to the extern "system" trampoline and aborts the process.

Taking a WaitableHandle rather than a bare handle is what keeps this constructor safe: the thread pool does not support every waitable object, and a mutex handle in particular is undefined rather than an error.

§Errors

Returns the error from CreateThreadpoolWait.

Source

pub fn handle(&self) -> BorrowedHandle<'_>

Borrow the watched handle, for signalling or inspecting it.

Source

pub fn arm(&self, timeout: Option<Duration>)

Arm the wait, so the next signal or timeout runs the callback once.

timeout of None waits indefinitely. Arming replaces any previous arming rather than adding to it, and an activation consumes the arming – rearm from inside the callback with WaitActivation::rearm to keep watching.

Source

pub fn disarm(&self)

Stop watching.

New activations stop being queued, but a callback already queued still runs; use ThreadpoolWait::cancel_pending to drop those as well.

Source

pub fn wait(&self)

Let every queued callback run, and block until none is executing.

This does not leave a self-re-arming wait idle: a callback running during this call can rearm before it returns, so the object is watching again when this returns. Use stop_and_drain to reach quiescence.

Source

pub fn cancel_pending(&self)

Drop callbacks that have not started, then wait for any executing one.

Like wait, this does not by itself leave a self-re-arming wait idle: it does not suppress the re-arm of a callback that is already running. Use stop_and_drain when the wait must actually be quiescent afterwards.

Source

pub fn stop_and_drain(&self)

Stop watching and block until the wait is idle, leaving it reusable.

This exists because neither disarm nor cancel_pending can stop a self-re-arming wait on its own: a callback already running can call WaitActivation::rearm after a disarm from outside has taken effect. This suppresses re-arming for the duration of the call, using the same mechanism Drop uses, and lifts the suppression before returning so the wait can be armed again.

§What this guarantees

On return, provided no other thread arms the wait during the call:

  • no callback is queued or executing, and
  • the object is not watching – a re-arm requested by a callback that ran during the call is discarded rather than deferred.
§What it does not

A concurrent arm from another thread is not excluded. ThreadpoolWait is Sync and arm takes &self, so it does not pass through the suppression this uses, and nothing in this crate orders such a call against this one. A caller needing the wait to be provably idle must ensure nothing else arms it for the duration, by owning it exclusively or serializing access to it.

Calling this from inside the wait’s own callback would deadlock, because it waits for that callback to finish.

Trait Implementations§

Source§

impl Drop for ThreadpoolWait

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Send for ThreadpoolWait

Source§

impl Sync for ThreadpoolWait

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
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, 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, <T as TryFrom<U>>::Error>

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.