Skip to content

Commit 3cfc527

Browse files
authored
Fixed memory leak on Apple platforms (#17)
1 parent 857354c commit 3cfc527

File tree

1 file changed

+37
-25
lines changed

1 file changed

+37
-25
lines changed

src/apple.rs

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,54 @@ extern crate libc;
22

33
use std::num::NonZeroUsize;
44

5-
use self::libc::{kern_return_t, mach_msg_type_number_t, mach_port_t, thread_t};
6-
7-
// This constant is from
8-
// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/
9-
// usr/include/mach/machine/thread_state.h.
10-
//
11-
// It has not been updated since Apple devices started to support 64-bit ARM (iOS), so it
12-
// should be very stable.
13-
const THREAD_STATE_MAX: i32 = 1296;
14-
#[allow(non_camel_case_types)]
15-
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L155
16-
type task_inspect_t = mach_port_t;
5+
use self::libc::{
6+
kern_return_t, mach_port_t, natural_t, task_threads, thread_act_array_t, vm_size_t,
7+
};
8+
179
#[allow(non_camel_case_types)]
18-
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L238
19-
type thread_array_t = [thread_t; THREAD_STATE_MAX as usize];
10+
// https://developer.apple.com/documentation/kernel/mach_port_name_t
11+
type mach_port_name_t = natural_t;
2012

2113
extern "C" {
22-
// https://developer.apple.com/documentation/kernel/1537751-task_threads/
23-
fn task_threads(
24-
target_task: task_inspect_t,
25-
act_list: *mut thread_array_t,
26-
act_listCnt: *mut mach_msg_type_number_t,
14+
// https://developer.apple.com/documentation/kernel/1402285-mach_vm_deallocate
15+
fn mach_vm_deallocate(
16+
target_task: mach_port_t,
17+
address: *mut u32,
18+
size: vm_size_t,
2719
) -> kern_return_t;
20+
21+
// https://developer.apple.com/documentation/kernel/1578777-mach_port_deallocate
22+
fn mach_port_deallocate(task: mach_port_t, name: mach_port_name_t) -> kern_return_t;
2823
}
2924

3025
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
31-
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_threads.html
32-
let mut thread_state = [0u32; THREAD_STATE_MAX as usize];
26+
// https://developer.apple.com/documentation/kernel/1537751-task_threads
27+
let mut thread_list: thread_act_array_t = std::ptr::null_mut();
3328
let mut thread_count = 0;
3429

35-
// Safety: `mach_task_self` always returns a valid value, `thread_state` is large enough, and
36-
// both it and `thread_count` are writable.
37-
let result =
38-
unsafe { task_threads(libc::mach_task_self(), &mut thread_state, &mut thread_count) };
30+
// Safety:
31+
// - `mach_task_self` always returns a valid value,
32+
// - `thread_list` is a pointer that will point to kernel allocated memory that needs to be
33+
// deallocated if the call succeeds
34+
let task = unsafe { libc::mach_task_self() };
35+
let result = unsafe { task_threads(task, &mut thread_list, &mut thread_count) };
3936

4037
if result == libc::KERN_SUCCESS {
38+
// Deallocate the mach port rights for the threads
39+
for thread in 0..thread_count {
40+
unsafe {
41+
mach_port_deallocate(task, *(thread_list.offset(thread as isize)) as u32);
42+
}
43+
}
44+
// Deallocate the thread list
45+
unsafe {
46+
mach_vm_deallocate(
47+
task,
48+
thread_list,
49+
std::mem::size_of::<mach_port_t>() * thread_count as usize,
50+
);
51+
}
52+
4153
NonZeroUsize::new(thread_count as usize)
4254
} else {
4355
None

0 commit comments

Comments
 (0)