use std::io; use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle, RawHandle}; use windows_sys::Win32::System::JobObjects::*; pub(crate) struct Job(OwnedHandle); impl Job { pub fn new(flags: u32) -> io::Result { let handle = unsafe { CreateJobObjectW(std::ptr::null(), std::ptr::null()) }; if handle.is_null() { return Err(io::Error::last_os_error()); } let job = Self(unsafe { OwnedHandle::from_raw_handle(handle) }); let mut limits: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { std::mem::zeroed() }; limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | flags; if unsafe { SetInformationJobObject( job.as_raw_handle(), JobObjectExtendedLimitInformation, (&raw const limits).cast(), std::mem::size_of_val(&limits) as u32, ) } == 0 { return Err(io::Error::last_os_error()); } Ok(job) } pub fn alive(&self) -> io::Result { let mut accounting: JOBOBJECT_BASIC_ACCOUNTING_INFORMATION = unsafe { std::mem::zeroed() }; if unsafe { QueryInformationJobObject( self.as_raw_handle(), JobObjectBasicAccountingInformation, (&raw mut accounting).cast(), std::mem::size_of_val(&accounting) as u32, std::ptr::null_mut(), ) } == 0 { return Err(io::Error::last_os_error()); } Ok(accounting.ActiveProcesses > 0) } pub fn terminate(&self) -> io::Result { if !self.alive()? { return Ok(false); } if unsafe { TerminateJobObject(self.as_raw_handle(), 1) } == 0 { return Err(io::Error::last_os_error()); } Ok(true) } } impl AsRawHandle for Job { fn as_raw_handle(&self) -> RawHandle { self.0.as_raw_handle() } }