Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
99182dd
std: use semaphore for thread parking on Apple platforms
joboet Oct 6, 2022
0ad4dd4
std: add thread parking tests
joboet Oct 6, 2022
bf135de
Clean up rustdoc startup.
nnethercote Oct 7, 2022
c461f3a
Merge `main_options` into `main_args`.
nnethercote Oct 7, 2022
d156a90
Inline and remove `scoped_thread`.
nnethercote Oct 7, 2022
226387a
Reduce visibility of some functions.
nnethercote Oct 7, 2022
c00937f
Inline and remove `create_compiler_and_run`.
nnethercote Oct 7, 2022
8067016
Apply `Lrc` later to `sess` and `codegen_backend`.
nnethercote Oct 7, 2022
1f0463a
Replace a `spawn_unchecked` with `spawn_scoped`.
nnethercote Oct 7, 2022
b4c8a7b
std: remove unused linker attribute
joboet Oct 8, 2022
c320ab9
std: do not use dispatch semaphore under miri (yet)
joboet Oct 8, 2022
76386bd
Make dyn* cast into a coercion
compiler-errors Sep 14, 2022
b841848
check if the self type is `ty::Float` before getting second substs
TaKO8Ki Oct 13, 2022
5378677
normalize stderr
TaKO8Ki Oct 13, 2022
feb4244
Allow dyn* upcasting
compiler-errors Sep 14, 2022
0cb217d
Remove CastCheckResult since it's unused
compiler-errors Sep 14, 2022
8c7e836
Address nits, add test for implicit dyn-star coercion without feature…
compiler-errors Oct 14, 2022
40bb4b7
Update cargo
weihanglo Oct 14, 2022
f528414
Add missing checks for `doc(cfg_hide(...))` attribute
GuillaumeGomez Oct 12, 2022
6f0c247
Add UI test for invalid `doc(cfg_hide(...))` attributes
GuillaumeGomez Oct 12, 2022
062ea9c
remove no_core feature
TaKO8Ki Oct 14, 2022
0c5f581
Rollup merge of #101832 - compiler-errors:dyn-star-plus, r=eholk
Dylan-DPC Oct 15, 2022
3654dc7
Rollup merge of #102769 - nnethercote:rustdoc-startup, r=jyn514
Dylan-DPC Oct 15, 2022
eb5863a
Rollup merge of #102773 - joboet:apple_parker, r=thomcc
Dylan-DPC Oct 15, 2022
97560c7
Rollup merge of #102954 - GuillaumeGomez:cfg-hide-attr-checks, r=Mani…
Dylan-DPC Oct 15, 2022
de5e4e8
Rollup merge of #103003 - TaKO8Ki:fix-102989, r=compiler-errors
Dylan-DPC Oct 15, 2022
8437d75
Rollup merge of #103041 - weihanglo:update-cargo, r=ehuss
Dylan-DPC Oct 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions library/std/src/sys/unix/thread_parker/darwin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//! Thread parking for Darwin-based systems.
//!
//! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they
//! cannot be used in `std` because they are non-public (their use will lead to
//! rejection from the App Store) and because they are only available starting
//! with macOS version 10.12, even though the minimum target version is 10.7.
//!
//! Therefore, we need to look for other synchronization primitives. Luckily, Darwin
//! supports semaphores, which allow us to implement the behaviour we need with
//! only one primitive (as opposed to a mutex-condvar pair). We use the semaphore
//! provided by libdispatch, as the underlying Mach semaphore is only dubiously
//! public.

use crate::pin::Pin;
use crate::sync::atomic::{
AtomicI8,
Ordering::{Acquire, Release},
};
use crate::time::Duration;

type dispatch_semaphore_t = *mut crate::ffi::c_void;
type dispatch_time_t = u64;

const DISPATCH_TIME_NOW: dispatch_time_t = 0;
const DISPATCH_TIME_FOREVER: dispatch_time_t = !0;

// Contained in libSystem.dylib, which is linked by default.
extern "C" {
fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t;
fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t;
fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize;
fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) -> isize;
fn dispatch_release(object: *mut crate::ffi::c_void);
}

const EMPTY: i8 = 0;
const NOTIFIED: i8 = 1;
const PARKED: i8 = -1;

pub struct Parker {
semaphore: dispatch_semaphore_t,
state: AtomicI8,
}

unsafe impl Sync for Parker {}
unsafe impl Send for Parker {}

impl Parker {
pub unsafe fn new(parker: *mut Parker) {
let semaphore = dispatch_semaphore_create(0);
assert!(
!semaphore.is_null(),
"failed to create dispatch semaphore for thread synchronization"
);
parker.write(Parker { semaphore, state: AtomicI8::new(EMPTY) })
}

// Does not need `Pin`, but other implementation do.
pub unsafe fn park(self: Pin<&Self>) {
// The semaphore counter must be zero at this point, because unparking
// threads will not actually increase it until we signalled that we
// are waiting.

// Change NOTIFIED to EMPTY and EMPTY to PARKED.
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
return;
}

// Another thread may increase the semaphore counter from this point on.
// If it is faster than us, we will decrement it again immediately below.
// If we are faster, we wait.

// Ensure that the semaphore counter has actually been decremented, even
// if the call timed out for some reason.
while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}

// At this point, the semaphore counter is zero again.

// We were definitely woken up, so we don't need to check the state.
// Still, we need to reset the state using a swap to observe the state
// change with acquire ordering.
self.state.swap(EMPTY, Acquire);
}

// Does not need `Pin`, but other implementation do.
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
return;
}

let nanos = dur.as_nanos().try_into().unwrap_or(i64::MAX);
let timeout = dispatch_time(DISPATCH_TIME_NOW, nanos);

let timeout = dispatch_semaphore_wait(self.semaphore, timeout) != 0;

let state = self.state.swap(EMPTY, Acquire);
if state == NOTIFIED && timeout {
// If the state was NOTIFIED but semaphore_wait returned without
// decrementing the count because of a timeout, it means another
// thread is about to call semaphore_signal. We must wait for that
// to happen to ensure the semaphore count is reset.
while dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER) != 0 {}
} else {
// Either a timeout occurred and we reset the state before any thread
// tried to wake us up, or we were woken up and reset the state,
// making sure to observe the state change with acquire ordering.
// Either way, the semaphore counter is now zero again.
}
}

// Does not need `Pin`, but other implementation do.
pub fn unpark(self: Pin<&Self>) {
let state = self.state.swap(NOTIFIED, Release);
if state == PARKED {
unsafe {
dispatch_semaphore_signal(self.semaphore);
}
}
}
}

impl Drop for Parker {
fn drop(&mut self) {
// SAFETY:
// We always ensure that the semaphore count is reset, so this will
// never cause an exception.
unsafe {
dispatch_release(self.semaphore);
}
}
}
13 changes: 12 additions & 1 deletion library/std/src/sys/unix/thread_parker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@
)))]

cfg_if::cfg_if! {
if #[cfg(target_os = "netbsd")] {
if #[cfg(all(
any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos",
),
not(miri),
))] {
mod darwin;
pub use darwin::Parker;
} else if #[cfg(target_os = "netbsd")] {
mod netbsd;
pub use netbsd::Parker;
} else {
Expand Down
22 changes: 22 additions & 0 deletions library/std/src/thread/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,28 @@ fn test_try_panic_any_message_unit_struct() {
}
}

#[test]
fn test_park_unpark_before() {
for _ in 0..10 {
thread::current().unpark();
thread::park();
}
}

#[test]
fn test_park_unpark_called_other_thread() {
for _ in 0..10 {
let th = thread::current();

let _guard = thread::spawn(move || {
super::sleep(Duration::from_millis(50));
th.unpark();
});

thread::park();
}
}

#[test]
fn test_park_timeout_unpark_before() {
for _ in 0..10 {
Expand Down