diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4bd40a..237f4d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,9 +10,10 @@ jobs: name: create-release runs-on: windows-2019 steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 + - uses: actions/checkout@v3 + + - name: Check semver + uses: obi1kenobi/cargo-semver-checks-action@v2 - uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.toml b/Cargo.toml index c6e22f1..c5d85a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "win32job" -version = "1.0.3" +version = "2.0.0" authors = ["Ohad Ravid "] edition = "2021" license = "MIT OR Apache-2.0" diff --git a/src/job.rs b/src/job.rs index bd39543..a07327f 100644 --- a/src/job.rs +++ b/src/job.rs @@ -13,11 +13,11 @@ use crate::error::JobError; use crate::limits::ExtendedLimitInfo; use std::{ffi::c_void, mem}; -pub use crate::utils::{get_current_process, get_process_memory_info}; +pub use crate::utils::get_current_process; #[derive(Debug)] pub struct Job { - handle: HANDLE, + pub(crate) handle: HANDLE, } unsafe impl Send for Job {} @@ -32,9 +32,7 @@ impl Job { } /// Create an anonymous job object and sets it's limit according to `info`. - /// Note: This method shouldn't change the provided `info`, but the internal Windows API - /// require a mutable pointer, which means this function requires &mut as well. - pub fn create_with_limit_info(info: &mut ExtendedLimitInfo) -> Result { + pub fn create_with_limit_info(info: &ExtendedLimitInfo) -> Result { let job = Self::create()?; job.set_extended_limit_info(info)?; @@ -43,16 +41,16 @@ impl Job { /// Return the underlying handle to the job. /// Note that this handle will be closed once the `Job` object is dropped. - pub fn handle(&self) -> HANDLE { - self.handle + pub fn handle(&self) -> isize { + self.handle.0 } /// Return the underlying handle to the job, consuming the job. /// Note that the handle will NOT be closed, so it is the caller's responsibly to close it. - pub fn into_handle(self) -> HANDLE { + pub fn into_handle(self) -> isize { let job = mem::ManuallyDrop::new(self); - job.handle + job.handle.0 } /// Return basic and extended limit information for a job object. @@ -74,9 +72,7 @@ impl Job { } /// Set the basic and extended limit information for a job object. - /// Note: This method shouldn't change the provided `info`, but the internal Windows API - /// require a mutable pointer, which means this function requires &mut as well. - pub fn set_extended_limit_info(&self, info: &mut ExtendedLimitInfo) -> Result<(), JobError> { + pub fn set_extended_limit_info(&self, info: &ExtendedLimitInfo) -> Result<(), JobError> { unsafe { SetInformationJobObject( self.handle, @@ -90,8 +86,8 @@ impl Job { /// Assigns a process to the job object. /// See also [Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-assignprocesstojobobject). - pub fn assign_process(&self, proc_handle: HANDLE) -> Result<(), JobError> { - unsafe { AssignProcessToJobObject(self.handle, proc_handle) } + pub fn assign_process(&self, proc_handle: isize) -> Result<(), JobError> { + unsafe { AssignProcessToJobObject(self.handle, HANDLE(proc_handle)) } .map_err(|e| JobError::AssignFailed(e.into())) } diff --git a/src/lib.rs b/src/lib.rs index 8525522..280050a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ //! or by creating an empty one using `new()`, use helper methods to configure //! the required limits, and finally set the info to the job. //! -//! ```edition2018 +//! ```edition2021 //! use win32job::*; //! # fn main() -> Result<(), JobError> { //! @@ -28,7 +28,7 @@ //! ``` //! //! Which is equivalnent to: -//! ```edition2018 +//! ```edition2021 //! use win32job::*; //! # fn main() -> Result<(), JobError> { //! @@ -45,33 +45,6 @@ //! # Ok(()) //! # } //! ``` -//! -//! # Using the low level API -//! -//! The most basic API is getting an `ExtendedLimitInfo` object and -//! manipulating the raw `JOBOBJECT_BASIC_LIMIT_INFORMATION`, and then set it back to the job. -//! -//! It's important to remeber to set the needed `LimitFlags` for each limit used. -//! -//! ```edition2018 -//! use win32job::*; -//! # fn main() -> Result<(), JobError> { -//! use windows::Win32::System::JobObjects::JOB_OBJECT_LIMIT_WORKINGSET; -//! -//! let job = Job::create()?; -//! let mut info = job.query_extended_limit_info()?; -//! -//! 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)?; -//! job.assign_current_process()?; -//! # info.clear_limits(); -//! # job.set_extended_limit_info(&mut info)?; -//! # Ok(()) -//! # } -//! ``` mod error; mod job; mod limits; diff --git a/src/limits.rs b/src/limits.rs index a8e9c95..d36f70f 100644 --- a/src/limits.rs +++ b/src/limits.rs @@ -1,5 +1,3 @@ -use std::mem; - use windows::Win32::System::{ JobObjects::{ JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JOB_OBJECT_LIMIT_AFFINITY, @@ -12,8 +10,10 @@ use windows::Win32::System::{ }, }; -pub struct ExtendedLimitInfo(pub JOBOBJECT_EXTENDED_LIMIT_INFORMATION); +#[derive(Debug)] +pub struct ExtendedLimitInfo(pub(crate) JOBOBJECT_EXTENDED_LIMIT_INFORMATION); +#[derive(Debug, Clone, Copy)] #[repr(u32)] pub enum PriorityClass { Normal = NORMAL_PRIORITY_CLASS.0, @@ -36,7 +36,7 @@ impl Default for ExtendedLimitInfo { impl ExtendedLimitInfo { /// Return an empty extended info objects, without any limits. pub fn new() -> Self { - let inner: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() }; + let inner = Default::default(); ExtendedLimitInfo(inner) } @@ -126,7 +126,7 @@ mod tests { let memory_info = get_process_memory_info(get_current_process()).unwrap(); - assert!(memory_info.WorkingSetSize <= max * 2); + assert!(memory_info.working_set_size <= max * 2); info.clear_limits(); diff --git a/src/query.rs b/src/query.rs index 31b176d..7f2eb39 100644 --- a/src/query.rs +++ b/src/query.rs @@ -6,6 +6,7 @@ use windows::Win32::System::JobObjects::{ use crate::{Job, JobError}; #[repr(C)] +#[derive(Debug)] struct ProcessIdList { header: JOBOBJECT_BASIC_PROCESS_ID_LIST, list: [usize; 1024], @@ -20,13 +21,13 @@ impl Job { // This can be fixed by calling `QueryInformationJobObject` a second time, // with a bigger list with the correct size (as returned from the first call). let mut proc_id_list = ProcessIdList { - header: unsafe { mem::zeroed() }, + header: Default::default(), list: [0usize; 1024], }; unsafe { QueryInformationJobObject( - self.handle(), + self.handle, JobObjectBasicProcessIdList, &mut proc_id_list as *mut _ as *mut c_void, mem::size_of_val(&proc_id_list) as u32, @@ -35,9 +36,15 @@ impl Job { } .map_err(|e| JobError::GetInfoFailed(e.into()))?; - let list = &proc_id_list.list[..proc_id_list.header.NumberOfProcessIdsInList as usize]; + let list = proc_id_list + .header + .ProcessIdList + .into_iter() + .chain(proc_id_list.list) + .take(proc_id_list.header.NumberOfProcessIdsInList as usize) + .collect(); - Ok(list.to_vec()) + Ok(list) } } @@ -56,7 +63,11 @@ mod tests { let pids = job.query_process_id_list().unwrap(); + let current_process_id = std::process::id() as usize; + // It's not equal to 1 because sometime we "catch" `rusty_fork_test` sub procs. assert!(pids.len() >= 1); + + assert!(pids.contains(¤t_process_id)); } } diff --git a/src/utils.rs b/src/utils.rs index ab3143e..a840560 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,43 +3,66 @@ use std::{io, mem}; use windows::Win32::{ Foundation::HANDLE, System::{ - ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, + ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS_EX}, Threading::{GetCurrentProcess, GetProcessAffinityMask}, }, }; /// Return a pseudo handle to the current process. /// See also [Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess) for this function. -pub fn get_current_process() -> HANDLE { - unsafe { GetCurrentProcess() } +pub fn get_current_process() -> isize { + unsafe { GetCurrentProcess() }.0 +} + +#[derive(Debug, Clone)] +pub struct ProcessMemoryCounters { + pub page_fault_count: u32, + pub peak_working_set_size: usize, + pub working_set_size: usize, + pub quota_peak_paged_pool_usage: usize, + pub quota_paged_pool_usage: usize, + pub quota_peak_non_paged_pool_usage: usize, + pub quota_non_paged_pool_usage: usize, + pub pagefile_usage: usize, + pub peak_pagefile_usage: usize, + pub private_usage: usize, } /// Retrieves information about the memory usage of the specified process. /// See also [Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessmemoryinfo) for this function. -pub fn get_process_memory_info( - process_handle: HANDLE, -) -> Result { - let mut counters: PROCESS_MEMORY_COUNTERS = PROCESS_MEMORY_COUNTERS::default(); +pub fn get_process_memory_info(process_handle: isize) -> Result { + let mut counters = PROCESS_MEMORY_COUNTERS_EX::default(); unsafe { GetProcessMemoryInfo( - process_handle, - &mut counters as *mut _, - mem::size_of::() as u32, + HANDLE(process_handle), + &mut counters as *mut _ as *mut _, + mem::size_of_val(&counters) as u32, ) - } - .map_err(|e| e.into()) - .map(|_| counters) + }?; + + Ok(ProcessMemoryCounters { + page_fault_count: counters.PageFaultCount, + peak_working_set_size: counters.PeakWorkingSetSize, + working_set_size: counters.WorkingSetSize, + quota_peak_paged_pool_usage: counters.QuotaPeakPagedPoolUsage, + quota_paged_pool_usage: counters.QuotaPagedPoolUsage, + quota_peak_non_paged_pool_usage: counters.QuotaPeakNonPagedPoolUsage, + quota_non_paged_pool_usage: counters.QuotaNonPagedPoolUsage, + pagefile_usage: counters.PagefileUsage, + peak_pagefile_usage: counters.PeakPagefileUsage, + private_usage: counters.PrivateUsage, + }) } /// Retrieves the process affinity mask for the specified process and the system affinity mask for the system. /// See also [Microsoft Docs](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask) for this function. -pub fn get_process_affinity_mask(process_handle: HANDLE) -> Result<(usize, usize), io::Error> { +pub fn get_process_affinity_mask(process_handle: isize) -> Result<(usize, usize), io::Error> { let mut process_affinity_mask = 0usize; let mut system_affinity_mask = 0usize; unsafe { GetProcessAffinityMask( - process_handle, + HANDLE(process_handle), &mut process_affinity_mask as *mut _, &mut system_affinity_mask as *mut _, )