Skip to content

Commit 9678cec

Browse files
committed
std: rewrite SGX thread parker
1 parent abace0a commit 9678cec

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

library/std/src/sys/sgx/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub mod process;
3333
pub mod stdio;
3434
pub mod thread;
3535
pub mod thread_local_key;
36+
pub mod thread_parker;
3637
pub mod time;
3738

3839
mod condvar;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//! Thread parking based on SGX events.
2+
3+
use super::abi::{thread, usercalls};
4+
use crate::io::ErrorKind;
5+
use crate::pin::Pin;
6+
use crate::ptr::{self, NonNull};
7+
use crate::sync::atomic::AtomicPtr;
8+
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
9+
use crate::time::Duration;
10+
use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
11+
12+
const EMPTY: *mut u8 = ptr::invalid_mut(0);
13+
/// The TCS structure must be page-aligned, so this cannot be a valid pointer
14+
const NOTIFIED: *mut u8 = ptr::invalid_mut(1);
15+
16+
pub struct Parker {
17+
state: AtomicPtr<u8>,
18+
}
19+
20+
impl Parker {
21+
/// Construct the thread parker. The UNIX parker implementation
22+
/// requires this to happen in-place.
23+
pub unsafe fn new(parker: *mut Parker) {
24+
unsafe { parker.write(Parker::new_internal()) }
25+
}
26+
27+
pub(super) fn new_internal() -> Parker {
28+
Parker { state: AtomicPtr::new(EMPTY) }
29+
}
30+
31+
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
32+
pub unsafe fn park(self: Pin<&Self>) {
33+
let tcs = thread::current().as_ptr();
34+
35+
if self.state.load(Acquire) != NOTIFIED {
36+
if self.state.compare_exchange(EMPTY, tcs, Acquire, Acquire).is_ok() {
37+
// Loop to guard against spurious wakeups.
38+
loop {
39+
let event = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
40+
assert!(event & EV_UNPARK == EV_UNPARK);
41+
if self.state.load(Acquire) == NOTIFIED {
42+
break;
43+
}
44+
}
45+
}
46+
}
47+
48+
// At this point, the token was definately read with acquire ordering,
49+
// so this can be a store.
50+
self.state.store(EMPTY, Relaxed);
51+
}
52+
53+
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
54+
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
55+
let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
56+
let tcs = thread::current().as_ptr();
57+
58+
if self.state.load(Acquire) != NOTIFIED {
59+
if self.state.compare_exchange(EMPTY, tcs, Acquire, Acquire).is_ok() {
60+
match usercalls::wait(EV_UNPARK, timeout) {
61+
Ok(event) => assert!(event & EV_UNPARK == EV_UNPARK),
62+
Err(e) => {
63+
assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
64+
}
65+
}
66+
67+
// Swap to provide acquire ordering even if the timeout occurred
68+
// before the token was set. This situation can result in spurious
69+
// wakeups on the next call to `park_timeout`, but it is better to let
70+
// those be handled by the user than do some perhaps unnecessary, but
71+
// always expensive guarding.
72+
self.state.swap(EMPTY, Acquire);
73+
return;
74+
}
75+
}
76+
77+
// The token was already read with `acquire` ordering, this can be a store.
78+
self.state.store(EMPTY, Relaxed);
79+
}
80+
81+
// This implementation doesn't require `Pin`, but other implementations do.
82+
pub fn unpark(self: Pin<&Self>) {
83+
let state = self.state.swap(NOTIFIED, Release);
84+
85+
if !matches!(state, EMPTY | NOTIFIED) {
86+
// There is a thread waiting, wake it up.
87+
let tcs = NonNull::new(state).unwrap();
88+
// This will fail if the thread has already terminated by the time the signal is send,
89+
// but that is OK.
90+
let _ = usercalls::send(EV_UNPARK, Some(tcs));
91+
}
92+
}
93+
}

library/std/src/sys_common/thread_parker/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ cfg_if::cfg_if! {
1313
pub use crate::sys::thread_parker::Parker;
1414
} else if #[cfg(target_family = "unix")] {
1515
pub use crate::sys::thread_parker::Parker;
16+
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
17+
pub use crate::sys::thread_parker::Parker;
1618
} else {
1719
mod generic;
1820
pub use generic::Parker;

0 commit comments

Comments
 (0)