use std::os::windows::io::AsRawHandle;
use std::process::Child;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
SetInformationJobObject, TerminateJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_ACTIVE_PROCESS, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
};
use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_TERMINATE};
use crate::process_windows::os_error;
pub const DEFAULT_ACTIVE_PROCESS_LIMIT: u32 = 64;
pub struct WindowsJob {
handle: HANDLE,
}
unsafe impl Send for WindowsJob {}
unsafe impl Sync for WindowsJob {}
impl WindowsJob {
pub fn new(active_process_limit: Option<u32>) -> std::io::Result<Self> {
if active_process_limit == Some(0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"active process cap must be at least 1",
));
}
let handle = unsafe { CreateJobObjectW(None, None) }.map_err(os_error)?;
let job = Self { handle };
let mut limits = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if let Some(cap) = active_process_limit {
limits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
limits.BasicLimitInformation.ActiveProcessLimit = cap;
}
unsafe {
SetInformationJobObject(
job.handle,
JobObjectExtendedLimitInformation,
&limits as *const _ as *const std::ffi::c_void,
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
)
}
.map_err(os_error)?;
Ok(job)
}
pub fn assign_child(&self, child: &Child) -> std::io::Result<()> {
let process = HANDLE(child.as_raw_handle());
unsafe { AssignProcessToJobObject(self.handle, process) }.map_err(os_error)
}
pub fn assign_process_id(&self, pid: u32) -> std::io::Result<()> {
let process = unsafe { OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, false, pid) }
.map_err(os_error)?;
let assigned = unsafe { AssignProcessToJobObject(self.handle, process) };
unsafe {
let _ = CloseHandle(process);
}
assigned.map_err(os_error)
}
pub fn terminate(&self) -> std::io::Result<()> {
unsafe { TerminateJobObject(self.handle, 1) }.map_err(os_error)
}
}
impl Drop for WindowsJob {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.handle);
}
}
}