Skip to main content

windows_threadpool_sys/
lib.rs

1// Copyright (c) 2026 Mike Grier
2//! Memory-safe access to the Windows thread pool APIs.
3//!
4//! The Windows thread pool integrates work, timers, waits, and asynchronous I/O
5//! with the operating system's own scheduling facilities. Its distinguishing
6//! property is that an idle workload costs no threads at all: the pool and the
7//! kernel cooperate so a process waiting on timers, events, or I/O holds no
8//! dedicated thread stacks. This crate wraps those facilities while making
9//! callback and resource lifetimes explicit in Rust.
10//!
11//! # The object types
12//!
13//! Each thread-pool object is an owned Rust type whose `Drop` performs the
14//! documented teardown for that object, so callbacks can never outlive the state
15//! they capture:
16//!
17//! | Type | Wraps | Runs the callback when |
18//! |---|---|---|
19//! | [`work::ThreadpoolWork`] | `TP_WORK` | you submit it |
20//! | [`timer::ThreadpoolTimer`] | `TP_TIMER` | a due time arrives, once per arming |
21//! | [`timer::ThreadpoolPeriodicTimer`] | `TP_TIMER` | every period, until stopped |
22//! | [`wait::ThreadpoolWait`] | `TP_WAIT` | a handle signals or a wait times out |
23//! | [`io::ThreadpoolIo`] | `TP_IO` | an overlapped operation completes |
24//!
25//! One-shot and periodic timers are separate types on purpose. The platform
26//! models both with one object and a `period` argument, which hides the property
27//! that matters most when writing the callback: a [`timer::ThreadpoolPeriodicTimer`] may
28//! queue its next tick while the previous one is still running, so its callback
29//! must tolerate overlapping with itself, whereas a [`timer::ThreadpoolTimer`]
30//! re-armed from *inside* its callback never does -- that request is applied
31//! only once the callback returns. Arming a one-shot from outside while its
32//! callback runs can still overlap it; see the [`timer`] module for both the
33//! choice and that distinction.
34//!
35//! Three supporting types shape where those callbacks run and how they are torn
36//! down: [`pool::ThreadpoolPool`] is an owned private pool,
37//! [`callback_env::CallbackEnviron`] is the environment that selects a pool and
38//! a callback priority when an object is created, and
39//! [`cleanup_group::CleanupGroup`] releases many objects in one step instead of
40//! dropping each individually.
41//!
42//! # Submitting work
43//!
44//! ```
45//! use std::sync::Arc;
46//! use std::sync::atomic::{AtomicUsize, Ordering};
47//! use windows_threadpool_sys::work::ThreadpoolWork;
48//!
49//! let count = Arc::new(AtomicUsize::new(0));
50//! let counter = Arc::clone(&count);
51//!
52//! let work = ThreadpoolWork::new(move || {
53//!     counter.fetch_add(1, Ordering::SeqCst);
54//! }, None)?;
55//!
56//! for _ in 0..4 {
57//!     work.submit();
58//! }
59//! work.wait();
60//!
61//! assert_eq!(count.load(Ordering::SeqCst), 4);
62//! # Ok::<(), std::io::Error>(())
63//! ```
64//!
65//! # Running callbacks on a private pool
66//!
67//! A [`pool::ThreadpoolPool`] bounds the threads a subsystem may consume.
68//! Declare the pool before the objects that use it, so it is dropped last.
69//!
70//! ```
71//! use std::sync::Arc;
72//! use std::sync::atomic::{AtomicUsize, Ordering};
73//! use windows_threadpool_sys::callback_env::CallbackEnviron;
74//! use windows_threadpool_sys::pool::ThreadpoolPool;
75//! use windows_threadpool_sys::work::ThreadpoolWork;
76//!
77//! let pool = ThreadpoolPool::new()?;
78//! pool.set_max_threads(2)?;
79//!
80//! let mut env = CallbackEnviron::new();
81//! env.set_pool(&pool);
82//!
83//! let count = Arc::new(AtomicUsize::new(0));
84//! let counter = Arc::clone(&count);
85//! let work = ThreadpoolWork::new(move || {
86//!     counter.fetch_add(1, Ordering::SeqCst);
87//! }, Some(&mut env))?;
88//!
89//! work.submit();
90//! work.wait();
91//! assert_eq!(count.load(Ordering::SeqCst), 1);
92//! # Ok::<(), std::io::Error>(())
93//! ```
94//!
95//! # Callback rules
96//!
97//! Callbacks run on shared, process-managed threads, so every object type here
98//! holds its callback to the same contract:
99//!
100//! - It must restore any thread-local or thread state it changes before
101//!   returning, and must not terminate its thread.
102//! - It must not block waiting on its own object's rundown, which would wait on
103//!   itself.
104//! - It must not panic. A panic unwinds to the `extern "system"` trampoline,
105//!   where an escaping unwind aborts the process; nothing contains it. The panic
106//!   hook still runs first, so the message and location reach stderr by default
107//!   -- what is given up is the process, not the diagnostic. A callback that can
108//!   fail must handle its own errors rather than panicking.
109//!
110//! # Relationship to `windows-overlapped-io-sys`
111//!
112//! Thread-pool I/O is one of three completion backends for the overlapped model
113//! defined by [`windows-overlapped-io-sys`]. This crate implements the `TP_IO`
114//! backend over that crate's endpoint ownership and pinned operation storage,
115//! adding the balanced `StartThreadpoolIo` accounting that only the thread pool
116//! requires. The pool's internal completion port is never exposed.
117//!
118//! [`windows-overlapped-io-sys`]: https://docs.rs/windows-overlapped-io-sys
119//!
120//! # Status
121//!
122//! The crate is in active development. Work, timers, waits, private pools,
123//! cleanup groups, and thread-pool I/O are implemented and tested.
124//!
125//! Thread-pool I/O is deliberately not a cleanup-group member: a `TP_IO` object
126//! must not be closed while an overlapped operation is outstanding, and a bulk
127//! release cannot satisfy that. See [`cleanup_group`] for the reasoning.
128
129#![warn(missing_docs)]
130
131// Every module wraps a Win32 thread-pool object, so the whole public surface is
132// gated on Windows and the crate resolves to an empty one elsewhere. This
133// matches the sibling `windows-overlapped-io-sys`, and it is what lets a
134// cross-platform dependency tree name this crate unconditionally instead of
135// failing to compile on other targets.
136#[cfg(windows)]
137pub mod callback_env;
138#[cfg(windows)]
139pub mod cleanup_group;
140#[cfg(windows)]
141pub mod io;
142#[cfg(windows)]
143pub mod pool;
144#[cfg(windows)]
145pub mod timer;
146#[cfg(windows)]
147pub mod wait;
148#[cfg(windows)]
149pub mod work;