use windows::{
core::PCWSTR,
Win32::{
Foundation::{CloseHandle, HANDLE},
System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
QueryInformationJobObject, SetInformationJobObject,
},
},
};
use crate::error::JobError;
use crate::limits::ExtendedLimitInfo;
use std::{ffi::c_void, mem};
pub use crate::utils::get_current_process;
#[derive(Debug)]
pub struct Job {
pub(crate) handle: HANDLE,
}
unsafe impl Send for Job {}
unsafe impl Sync for Job {}
impl Job {
pub fn create() -> Result<Self, JobError> {
unsafe { CreateJobObjectW(None, PCWSTR::null()) }
.map_err(|e| JobError::CreateFailed(e.into()))
.map(|handle| Self { handle })
}
pub fn create_with_limit_info(info: &ExtendedLimitInfo) -> Result<Self, JobError> {
let job = Self::create()?;
job.set_extended_limit_info(info)?;
Ok(job)
}
pub fn handle(&self) -> isize {
self.handle.0 as _
}
pub fn into_handle(self) -> isize {
let job = mem::ManuallyDrop::new(self);
job.handle.0 as _
}
pub fn query_extended_limit_info(&self) -> Result<ExtendedLimitInfo, JobError> {
let mut info = ExtendedLimitInfo::default();
unsafe {
QueryInformationJobObject(
Some(self.handle),
JobObjectExtendedLimitInformation,
&mut info.0 as *mut _ as *mut c_void,
mem::size_of_val(&info.0) as u32,
None,
)
}
.map_err(|e| JobError::GetInfoFailed(e.into()))?;
Ok(info)
}
pub fn set_extended_limit_info(&self, info: &ExtendedLimitInfo) -> Result<(), JobError> {
unsafe {
SetInformationJobObject(
self.handle,
JobObjectExtendedLimitInformation,
&info.0 as *const _ as *const c_void,
mem::size_of_val(&info.0) as u32,
)
.map_err(|e| JobError::SetInfoFailed(e.into()))
}
}
pub fn assign_process(&self, proc_handle: isize) -> Result<(), JobError> {
unsafe { AssignProcessToJobObject(self.handle, HANDLE(proc_handle as _)) }
.map_err(|e| JobError::AssignFailed(e.into()))
}
pub fn assign_current_process(&self) -> Result<(), JobError> {
let current_proc_handle = get_current_process();
self.assign_process(current_proc_handle)
}
}
impl Drop for Job {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.handle);
}
}
}
#[cfg(test)]
mod tests {
use windows::Win32::System::JobObjects::JOB_OBJECT_LIMIT_WORKINGSET;
use crate::Job;
#[test]
fn it_works() {
let job = Job::create().unwrap();
let mut info = job.query_extended_limit_info().unwrap();
assert_eq!(info.0.BasicLimitInformation.LimitFlags.0, 0);
assert_eq!(info.0.BasicLimitInformation.SchedulingClass, 5);
info.0.BasicLimitInformation.MinimumWorkingSetSize = 1 * 1024 * 1024;
info.0.BasicLimitInformation.MaximumWorkingSetSize = 4 * 1024 * 1024;
info.0.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_WORKINGSET;
job.set_extended_limit_info(&mut info).unwrap();
info.0.BasicLimitInformation.LimitFlags.0 = 0;
job.set_extended_limit_info(&mut info).unwrap();
}
}