Skip to content

Instantly share code, notes, and snippets.

@joboet
Last active April 26, 2022 09:56
Show Gist options
  • Save joboet/3758f6653f2dd28185e5af9b40f4d9d7 to your computer and use it in GitHub Desktop.
Save joboet/3758f6653f2dd28185e5af9b40f4d9d7 to your computer and use it in GitHub Desktop.
macOS semaphore-based thread parker
#![allow(non_camel_case_types)]
use std::{
mem::MaybeUninit,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc,
},
thread,
time::Duration,
};
type mach_port_t = u32;
type semaphore_t = mach_port_t;
type task_t = mach_port_t;
type kern_return_t = u32;
#[repr(C)]
#[derive(Clone, Copy)]
struct mach_timespec_t {
sec: u32,
nsec: i32,
}
const SYNC_POLICY_FIFO: u32 = 0;
const KERN_OPERATION_TIMED_OUT: kern_return_t = 49;
extern "C" {
fn semaphore_create(
task: task_t,
sem: *mut semaphore_t,
policy: u32,
value: u32,
) -> kern_return_t;
fn semaphore_destroy(task: task_t, sem: semaphore_t) -> kern_return_t;
fn semaphore_wait(sem: semaphore_t) -> kern_return_t;
fn semaphore_timedwait(sem: semaphore_t, dur: mach_timespec_t) -> kern_return_t;
fn semaphore_signal(sem: semaphore_t) -> kern_return_t;
static mach_task_self_: mach_port_t;
}
struct Parker {
wake: AtomicBool,
sem: semaphore_t,
}
impl Parker {
fn new() -> Parker {
let sem = unsafe {
let mut sem = MaybeUninit::<semaphore_t>::uninit();
let r = semaphore_create(mach_task_self_, sem.as_mut_ptr(), SYNC_POLICY_FIFO, 0);
assert_eq!(r, 0);
sem.assume_init()
};
Parker {
wake: AtomicBool::new(true),
sem,
}
}
fn park(&self) {
unsafe {
semaphore_wait(self.sem);
}
self.wake.store(true, SeqCst);
}
fn park_timeout(&self, dur: Duration) {
let dur = mach_timespec_t {
// Truncate here because spurious wakeups are allowed.
sec: dur.as_secs().min(u32::MAX as u64) as u32,
// Always less than 1 billion, will never overflow.
nsec: dur.subsec_nanos() as i32,
};
let r = unsafe { semaphore_timedwait(self.sem, dur) };
if r != KERN_OPERATION_TIMED_OUT {
self.wake.store(true, SeqCst);
}
}
fn unpark(&self) {
let wake = self.wake.swap(false, SeqCst);
if wake {
unsafe {
semaphore_signal(self.sem);
}
}
}
}
impl Drop for Parker {
fn drop(&mut self) {
unsafe {
let r = semaphore_destroy(mach_task_self_, self.sem);
assert_eq!(r, 0);
}
}
}
fn main() {
let parker = Arc::new(Parker::new());
let waker = parker.clone();
thread::spawn(move || {
thread::sleep(Duration::from_secs(10));
waker.unpark();
});
parker.park_timeout(Duration::from_secs(1));
print!("success");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment