#[cfg(windows)]
use std::os::windows::io::AsRawHandle;
#[cfg(windows)]
use std::process::Child;
#[cfg(windows)]
use windows_sys::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation,
QueryInformationJobObject, SetInformationJobObject,
};
#[cfg(windows)]
use crate::error::ForgeError;
#[cfg(not(windows))]
pub(crate) struct ProcessJob;
#[cfg(windows)]
pub(crate) struct ProcessJob(windows_sys::Win32::Foundation::HANDLE);
#[cfg(not(windows))]
pub(crate) fn job_ref(_: &()) -> Option<&ProcessJob> {
None
}
#[cfg(windows)]
pub(crate) fn job_ref(job: &ProcessJob) -> Option<&ProcessJob> {
Some(job)
}
#[cfg(windows)]
impl ProcessJob {
pub(crate) fn assign(child: &Child) -> Result<Self, ForgeError> {
let handle = unsafe { CreateJobObjectW(std::ptr::null(), std::ptr::null()) };
if handle.is_null() {
return Err(os_error("failed to create the Windows Job Object"));
}
let job = Self(handle);
let mut information: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { std::mem::zeroed() };
information.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let configured = unsafe {
SetInformationJobObject(
handle,
JobObjectExtendedLimitInformation,
std::ptr::from_ref(&information).cast(),
std::mem::size_of_val(&information) as u32,
)
};
let process = child.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE;
if configured == 0 || unsafe { AssignProcessToJobObject(handle, process) } == 0 {
return Err(os_error("failed to configure the Windows Job Object"));
}
Ok(job)
}
pub(crate) fn terminate(&self) {
unsafe {
windows_sys::Win32::System::JobObjects::TerminateJobObject(self.0, 1);
}
}
pub(crate) fn peak_memory_mib(&self) -> Option<u64> {
let mut information: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { std::mem::zeroed() };
let success = unsafe {
QueryInformationJobObject(
self.0,
JobObjectExtendedLimitInformation,
std::ptr::from_mut(&mut information).cast(),
std::mem::size_of_val(&information) as u32,
std::ptr::null_mut(),
)
};
(success != 0).then_some(information.PeakJobMemoryUsed as u64 / 1024 / 1024)
}
}
#[cfg(windows)]
fn os_error(message: &str) -> ForgeError {
ForgeError::Command(format!("{message}:{}", std::io::Error::last_os_error()))
}
#[cfg(windows)]
impl Drop for ProcessJob {
fn drop(&mut self) {
unsafe {
windows_sys::Win32::Foundation::CloseHandle(self.0);
}
}
}