Skip to main content

Crate windows_threadpool_sys

Crate windows_threadpool_sys 

Source
Expand description

Memory-safe access to the Windows thread pool APIs.

The Windows thread pool integrates work, timers, waits, and asynchronous I/O with the operating system’s own scheduling facilities. Its distinguishing property is that an idle workload costs no threads at all: the pool and the kernel cooperate so a process waiting on timers, events, or I/O holds no dedicated thread stacks. This crate wraps those facilities while making callback and resource lifetimes explicit in Rust.

§The object types

Each thread-pool object is an owned Rust type whose Drop performs the documented teardown for that object, so callbacks can never outlive the state they capture:

TypeWrapsRuns the callback when
work::ThreadpoolWorkTP_WORKyou submit it
timer::ThreadpoolTimerTP_TIMERa due time arrives, once per arming
timer::ThreadpoolPeriodicTimerTP_TIMERevery period, until stopped
wait::ThreadpoolWaitTP_WAITa handle signals or a wait times out
io::ThreadpoolIoTP_IOan overlapped operation completes

One-shot and periodic timers are separate types on purpose. The platform models both with one object and a period argument, which hides the property that matters most when writing the callback: a timer::ThreadpoolPeriodicTimer may queue its next tick while the previous one is still running, so its callback must tolerate overlapping with itself, whereas a timer::ThreadpoolTimer re-armed from inside its callback never does – that request is applied only once the callback returns. Arming a one-shot from outside while its callback runs can still overlap it; see the timer module for both the choice and that distinction.

Three supporting types shape where those callbacks run and how they are torn down: pool::ThreadpoolPool is an owned private pool, callback_env::CallbackEnviron is the environment that selects a pool and a callback priority when an object is created, and cleanup_group::CleanupGroup releases many objects in one step instead of dropping each individually.

§Submitting work

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use windows_threadpool_sys::work::ThreadpoolWork;

let count = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&count);

let work = ThreadpoolWork::new(move || {
    counter.fetch_add(1, Ordering::SeqCst);
}, None)?;

for _ in 0..4 {
    work.submit();
}
work.wait();

assert_eq!(count.load(Ordering::SeqCst), 4);

§Running callbacks on a private pool

A pool::ThreadpoolPool bounds the threads a subsystem may consume. Declare the pool before the objects that use it, so it is dropped last.

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use windows_threadpool_sys::callback_env::CallbackEnviron;
use windows_threadpool_sys::pool::ThreadpoolPool;
use windows_threadpool_sys::work::ThreadpoolWork;

let pool = ThreadpoolPool::new()?;
pool.set_max_threads(2)?;

let mut env = CallbackEnviron::new();
env.set_pool(&pool);

let count = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&count);
let work = ThreadpoolWork::new(move || {
    counter.fetch_add(1, Ordering::SeqCst);
}, Some(&mut env))?;

work.submit();
work.wait();
assert_eq!(count.load(Ordering::SeqCst), 1);

§Callback rules

Callbacks run on shared, process-managed threads, so every object type here holds its callback to the same contract:

  • It must restore any thread-local or thread state it changes before returning, and must not terminate its thread.
  • It must not block waiting on its own object’s rundown, which would wait on itself.
  • It must not panic. A panic unwinds to the extern "system" trampoline, where an escaping unwind aborts the process; nothing contains it. The panic hook still runs first, so the message and location reach stderr by default – what is given up is the process, not the diagnostic. A callback that can fail must handle its own errors rather than panicking.

§Relationship to windows-overlapped-io-sys

Thread-pool I/O is one of three completion backends for the overlapped model defined by windows-overlapped-io-sys. This crate implements the TP_IO backend over that crate’s endpoint ownership and pinned operation storage, adding the balanced StartThreadpoolIo accounting that only the thread pool requires. The pool’s internal completion port is never exposed.

§Status

The crate is in active development. Work, timers, waits, private pools, cleanup groups, and thread-pool I/O are implemented and tested.

Thread-pool I/O is deliberately not a cleanup-group member: a TP_IO object must not be closed while an overlapped operation is outstanding, and a bulk release cannot satisfy that. See cleanup_group for the reasoning.

Modules§

callback_env
SDK-equivalent helpers for [TP_CALLBACK_ENVIRON_V3].
cleanup_group
Cleanup groups: releasing many thread-pool objects in one step.
io
Thread-pool I/O (TP_IO): a completion backend over the shared overlapped submission seam.
pool
Owned private thread pools: CreateThreadpool / CloseThreadpool.
timer
Thread-pool timers: CreateThreadpoolTimer / SetThreadpoolTimer / WaitForThreadpoolTimerCallbacks / CloseThreadpoolTimer.
wait
Thread-pool waits: CreateThreadpoolWait / SetThreadpoolWait / WaitForThreadpoolWaitCallbacks / CloseThreadpoolWait.
work
Thread-pool work objects: CreateThreadpoolWork / SubmitThreadpoolWork / WaitForThreadpoolWorkCallbacks / CloseThreadpoolWork.