use core::ffi::c_void;
use std::io;
use std::mem::ManuallyDrop;
use std::ptr;
use windows_sys::Win32::Foundation::{FALSE, TRUE};
use windows_sys::Win32::System::Threading::{
CloseThreadpoolWork, CreateThreadpoolWork, PTP_CALLBACK_INSTANCE, PTP_WORK,
SubmitThreadpoolWork, WaitForThreadpoolWorkCallbacks,
};
use crate::callback_env::CallbackEnviron;
struct WorkContext {
f: Box<dyn Fn() + Send + Sync + 'static>,
}
unsafe extern "system" fn work_trampoline(
_instance: PTP_CALLBACK_INSTANCE,
context: *mut core::ffi::c_void,
_work: PTP_WORK,
) {
let ctx = unsafe { &*(context as *const WorkContext) };
(ctx.f)();
}
pub struct ThreadpoolWork {
handle: PTP_WORK,
ctx: *mut WorkContext,
}
unsafe impl Send for ThreadpoolWork {}
unsafe impl Sync for ThreadpoolWork {}
impl ThreadpoolWork {
pub fn new<F>(callback: F, env: Option<&mut CallbackEnviron>) -> io::Result<Self>
where
F: Fn() + Send + Sync + 'static,
{
let ctx = Box::into_raw(Box::new(WorkContext {
f: Box::new(callback),
}));
let env_ptr = env.map_or(ptr::null_mut(), |e| e.as_mut_ptr());
let handle = unsafe {
CreateThreadpoolWork(Some(work_trampoline), ctx.cast(), env_ptr.cast_const())
};
if handle == 0 {
unsafe { drop(Box::from_raw(ctx)) };
return Err(io::Error::last_os_error());
}
Ok(Self { handle, ctx })
}
pub fn submit(&self) {
unsafe { SubmitThreadpoolWork(self.handle) };
}
pub fn wait(&self) {
unsafe { WaitForThreadpoolWorkCallbacks(self.handle, FALSE) };
}
pub fn cancel_pending(&self) {
unsafe { WaitForThreadpoolWorkCallbacks(self.handle, TRUE) };
}
pub(crate) fn into_parts(self) -> (PTP_WORK, *mut c_void) {
let this = ManuallyDrop::new(self);
(this.handle, this.ctx.cast())
}
pub(crate) unsafe fn drop_context(context: *mut c_void) {
drop(unsafe { Box::from_raw(context.cast::<WorkContext>()) });
}
}
impl Drop for ThreadpoolWork {
fn drop(&mut self) {
unsafe {
WaitForThreadpoolWorkCallbacks(self.handle, FALSE);
CloseThreadpoolWork(self.handle);
drop(Box::from_raw(self.ctx));
}
}
}
#[cfg(test)]
mod tests;