use std::io;
use std::ptr;
use std::sync::Mutex;
use windows_sys::Win32::System::Threading::{
CloseThreadpool, CreateThreadpool, PTP_POOL, SetThreadpoolThreadMaximum,
SetThreadpoolThreadMinimum,
};
#[derive(Debug, Default)]
struct Limits {
minimum: Option<u32>,
maximum: Option<u32>,
}
#[derive(Debug)]
pub struct ThreadpoolPool {
pool: PTP_POOL,
limits: Mutex<Limits>,
}
unsafe impl Send for ThreadpoolPool {}
unsafe impl Sync for ThreadpoolPool {}
impl ThreadpoolPool {
pub fn new() -> io::Result<Self> {
let pool = unsafe { CreateThreadpool(ptr::null()) };
if pool == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self {
pool,
limits: Mutex::new(Limits::default()),
})
}
pub fn set_max_threads(&self, maximum: u32) -> io::Result<()> {
if maximum == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"a thread pool needs a maximum of at least one thread; a maximum of zero runs no \
callbacks at all and the native call cannot report it",
));
}
let mut limits = self.limits.lock().unwrap_or_else(|e| e.into_inner());
if let Some(minimum) = limits.minimum
&& maximum < minimum
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"a maximum of {maximum} is below this pool's minimum of {minimum}; Win32 \
would let the minimum win silently, so the conflict is refused instead"
),
));
}
unsafe { SetThreadpoolThreadMaximum(self.pool, maximum) };
limits.maximum = Some(maximum);
Ok(())
}
pub fn set_min_threads(&self, minimum: u32) -> io::Result<()> {
let mut limits = self.limits.lock().unwrap_or_else(|e| e.into_inner());
if let Some(maximum) = limits.maximum
&& minimum > maximum
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"a minimum of {minimum} exceeds this pool's maximum of {maximum}; Win32 \
would honour the minimum and annul the maximum silently, so the conflict \
is refused instead"
),
));
}
let ok = unsafe { SetThreadpoolThreadMinimum(self.pool, minimum) };
if ok == 0 {
return Err(io::Error::last_os_error());
}
limits.minimum = Some(minimum);
Ok(())
}
pub(crate) fn as_raw(&self) -> PTP_POOL {
self.pool
}
}
impl Drop for ThreadpoolPool {
fn drop(&mut self) {
unsafe { CloseThreadpool(self.pool) };
}
}
#[cfg(test)]
mod tests;