use std::io;
#[derive(Debug)]
pub(super) struct JobObject {
handle: windows_sys::Win32::Foundation::HANDLE,
}
unsafe impl Send for JobObject {}
unsafe impl Sync for JobObject {}
impl JobObject {
pub(super) fn new_with_child(child: &tokio::process::Child) -> io::Result<Self> {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::JobObjects::{AssignProcessToJobObject, CreateJobObjectW};
use windows_sys::Win32::System::Threading::{
OpenProcess, PROCESS_SET_QUOTA, PROCESS_TERMINATE,
};
let pid = child.id().ok_or_else(|| {
io::Error::other("child has no PID; it was already polled to completion")
})?;
let job = unsafe { CreateJobObjectW(std::ptr::null(), std::ptr::null()) };
if job.is_null() {
return Err(io::Error::last_os_error());
}
let proc_handle = unsafe { OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, 0, pid) };
if proc_handle.is_null() {
let err = io::Error::last_os_error();
unsafe {
CloseHandle(job);
}
return Err(err);
}
let assigned = unsafe { AssignProcessToJobObject(job, proc_handle) } != 0;
unsafe {
CloseHandle(proc_handle);
}
if !assigned {
let err = io::Error::last_os_error();
unsafe {
CloseHandle(job);
}
return Err(err);
}
Ok(Self { handle: job })
}
pub(super) fn terminate(&self, exit_code: u32) -> io::Result<()> {
use windows_sys::Win32::System::JobObjects::TerminateJobObject;
let ok = unsafe { TerminateJobObject(self.handle, exit_code) } != 0;
if !ok {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
impl Drop for JobObject {
fn drop(&mut self) {
use windows_sys::Win32::Foundation::CloseHandle;
unsafe {
CloseHandle(self.handle);
}
}
}