1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2026 Mike Grier
//! 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:
//!
//! | Type | Wraps | Runs the callback when |
//! |---|---|---|
//! | [`work::ThreadpoolWork`] | `TP_WORK` | you submit it |
//! | [`timer::ThreadpoolTimer`] | `TP_TIMER` | a due time arrives, once per arming |
//! | [`timer::ThreadpoolPeriodicTimer`] | `TP_TIMER` | every period, until stopped |
//! | [`wait::ThreadpoolWait`] | `TP_WAIT` | a handle signals or a wait times out |
//! | [`io::ThreadpoolIo`] | `TP_IO` | an 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);
//! # Ok::<(), std::io::Error>(())
//! ```
//!
//! # 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);
//! # Ok::<(), std::io::Error>(())
//! ```
//!
//! # 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.
//!
//! [`windows-overlapped-io-sys`]: https://docs.rs/windows-overlapped-io-sys
//!
//! # 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.
// Every module wraps a Win32 thread-pool object, so the whole public surface is
// gated on Windows and the crate resolves to an empty one elsewhere. This
// matches the sibling `windows-overlapped-io-sys`, and it is what lets a
// cross-platform dependency tree name this crate unconditionally instead of
// failing to compile on other targets.