use std::sync::mpsc;
use std::sync::{Arc, Condvar, Mutex, PoisonError};
use std::time::Duration;
use windows_threadpool_sys::callback_env::CallbackEnviron;
use windows_threadpool_sys::pool::ThreadpoolPool;
use windows_threadpool_sys::work::ThreadpoolWork;
const BOUND: Duration = Duration::from_secs(5);
pub(crate) fn one_thread_pool() -> ThreadpoolPool {
let pool = ThreadpoolPool::new().expect("create a pool");
pool.set_max_threads(1)
.expect("limit the pool to one thread");
pool
}
type Gate = Arc<(Mutex<bool>, Condvar)>;
pub(crate) struct Held {
gate: Gate,
work: ThreadpoolWork,
}
impl Held {
pub(crate) fn start(env: &mut CallbackEnviron<'_>) -> Self {
let gate: Gate = Arc::new((Mutex::new(false), Condvar::new()));
let (started_sender, started) = mpsc::channel();
let held = Arc::clone(&gate);
let work = ThreadpoolWork::new(
move || {
let _ = started_sender.send(());
let (open, opened) = &*held;
let mut open = open.lock().unwrap_or_else(PoisonError::into_inner);
while !*open {
open = opened.wait(open).unwrap_or_else(PoisonError::into_inner);
}
},
Some(env),
)
.expect("create the work item");
work.submit();
started
.recv_timeout(BOUND)
.expect("the pool's thread took the work item");
Self { gate, work }
}
pub(crate) fn release(&self) {
let (open, opened) = &*self.gate;
*open.lock().unwrap_or_else(PoisonError::into_inner) = true;
opened.notify_all();
}
}
impl Drop for Held {
fn drop(&mut self) {
self.release();
self.work.stop_and_drain();
}
}