blob: fc1889a735c6bc612d2c4a3041b09aaec1b7fc7d [file] [log] [blame]
extern crate libc;
use std::num::NonZeroUsize;
use self::libc::{
kern_return_t, mach_port_t, natural_t, task_threads, thread_act_array_t, vm_size_t,
};
#[allow(non_camel_case_types)]
// https://developer.apple.com/documentation/kernel/mach_port_name_t
type mach_port_name_t = natural_t;
extern "C" {
// https://developer.apple.com/documentation/kernel/1402285-mach_vm_deallocate
fn mach_vm_deallocate(
target_task: mach_port_t,
address: *mut u32,
size: vm_size_t,
) -> kern_return_t;
// https://developer.apple.com/documentation/kernel/1578777-mach_port_deallocate
fn mach_port_deallocate(task: mach_port_t, name: mach_port_name_t) -> kern_return_t;
}
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
// https://developer.apple.com/documentation/kernel/1537751-task_threads
let mut thread_list: thread_act_array_t = std::ptr::null_mut();
let mut thread_count = 0;
// Safety:
// - `mach_task_self` always returns a valid value,
// - `thread_list` is a pointer that will point to kernel allocated memory that needs to be
// deallocated if the call succeeds
let task = unsafe { libc::mach_task_self() };
let result = unsafe { task_threads(task, &mut thread_list, &mut thread_count) };
if result == libc::KERN_SUCCESS {
// Deallocate the mach port rights for the threads
for thread in 0..thread_count {
unsafe {
mach_port_deallocate(task, *(thread_list.offset(thread as isize)) as u32);
}
}
// Deallocate the thread list
unsafe {
mach_vm_deallocate(
task,
thread_list,
std::mem::size_of::<mach_port_t>() * thread_count as usize,
);
}
NonZeroUsize::new(thread_count as usize)
} else {
None
}
}