Skip to content

Commit 77a6080

Browse files
Use dispatch semaphores on apple, where POSIX semaphores are explicitly and officially broken
1 parent 15cc9e3 commit 77a6080

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ rust-version = "1.69.0"
1717
[target.'cfg(unix)'.dependencies]
1818
nix = { version = "0.30", default-features = false, features = ["fs", "signal"]}
1919

20+
[target.'cfg(target_vendor = "apple")'.dependencies]
21+
dispatch = "0.2"
22+
2023
[target.'cfg(windows)'.dependencies]
2124
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_Security", "Win32_System_Console"] }
2225

src/platform/unix/mod.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,43 @@
99

1010
use crate::error::Error as CtrlcError;
1111

12-
static mut SEMAPHORE: nix::libc::sem_t = unsafe { std::mem::zeroed() };
13-
const SEM_THREAD_SHARED: nix::libc::c_int = 0;
12+
#[cfg(not(target_vendor = "apple"))]
13+
#[allow(static_mut_refs)] // rust-version = "1.69.0"
14+
mod implementation {
15+
static mut SEMAPHORE: nix::libc::sem_t = unsafe { std::mem::zeroed() };
16+
const SEM_THREAD_SHARED: nix::libc::c_int = 0;
17+
18+
pub unsafe fn sem_init() {
19+
nix::libc::sem_init(&mut SEMAPHORE as *mut _, SEM_THREAD_SHARED, 0);
20+
}
21+
22+
pub unsafe fn sem_post() {
23+
// No errors apply. EOVERFLOW is hypothetically possible but it's equivalent to a success for our oneshot use-casse.
24+
let _ = nix::libc::sem_post(&mut SEMAPHORE as *mut _);
25+
}
26+
27+
pub unsafe fn sem_wait_forever() {
28+
// The only realistic error is EINTR, which is restartable.
29+
while nix::libc::sem_wait(&mut SEMAPHORE as *mut _) == -1 {}
30+
}
31+
}
32+
33+
#[cfg(target_vendor = "apple")]
34+
mod implementation {
35+
static mut SEMAPHORE: dispatch::ffi::dispatch_semaphore_t = std::ptr::null_mut();
36+
37+
pub unsafe fn sem_init() {
38+
SEMAPHORE = dispatch::ffi::dispatch_semaphore_create(0);
39+
}
40+
41+
pub unsafe fn sem_post() {
42+
dispatch::ffi::dispatch_semaphore_signal(SEMAPHORE);
43+
}
44+
45+
pub unsafe fn sem_wait_forever() {
46+
dispatch::ffi::dispatch_semaphore_wait(SEMAPHORE, dispatch::ffi::DISPATCH_TIME_FOREVER);
47+
}
48+
}
1449

1550
/// Platform specific error type
1651
pub type Error = nix::Error;
@@ -20,8 +55,7 @@ pub type Signal = nix::sys::signal::Signal;
2055

2156
extern "C" fn os_handler(_: nix::libc::c_int) {
2257
unsafe {
23-
// No errors apply. EOVERFLOW is hypothetically possible but it's equivalent to a success for our oneshot use-casse.
24-
let _ = nix::libc::sem_post(&raw mut SEMAPHORE);
58+
implementation::sem_post();
2559
}
2660
}
2761

@@ -37,7 +71,7 @@ extern "C" fn os_handler(_: nix::libc::c_int) {
3771
pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> {
3872
use nix::sys::signal;
3973

40-
nix::libc::sem_init(&raw mut SEMAPHORE, SEM_THREAD_SHARED, 0);
74+
implementation::sem_init();
4175

4276
let handler = signal::SigHandler::Handler(os_handler);
4377
#[cfg(not(target_os = "nto"))]
@@ -99,7 +133,6 @@ pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> {
99133
///
100134
#[inline]
101135
pub unsafe fn block_ctrl_c() -> Result<(), CtrlcError> {
102-
// The only realistic error is EINTR, which is restartable.
103-
while nix::libc::sem_wait(&raw mut SEMAPHORE) == -1 {}
136+
implementation::sem_wait_forever();
104137
Ok(())
105138
}

0 commit comments

Comments
 (0)