Skip to content

Commit

Permalink
Add SGX thread parker
Browse files Browse the repository at this point in the history
  • Loading branch information
faern committed Mar 18, 2019
1 parent 66d67bb commit 7545f72
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 1 deletion.
14 changes: 13 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,29 @@
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![cfg_attr(feature = "nightly", feature(sgx_platform))]

#[cfg(all(feature = "nightly", target_os = "linux"))]
#[path = "thread_parker/linux.rs"]
mod thread_parker;

#[cfg(all(unix, not(all(feature = "nightly", target_os = "linux"))))]
#[path = "thread_parker/unix.rs"]
mod thread_parker;

#[cfg(windows)]
#[path = "thread_parker/windows/mod.rs"]
mod thread_parker;
#[cfg(not(any(windows, unix)))]

#[cfg(all(feature = "nightly", target_vendor = "fortanix", target_env = "sgx"))]
#[path = "thread_parker/sgx.rs"]
mod thread_parker;

#[cfg(not(any(
windows,
unix,
all(feature = "nightly", target_vendor = "fortanix", target_env = "sgx")
)))]
#[path = "thread_parker/generic.rs"]
mod thread_parker;

Expand Down
105 changes: 105 additions & 0 deletions core/src/thread_parker/sgx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use core::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
use std::{
os::fortanix_sgx::{
thread::current as current_tcs,
usercalls::{
self,
raw::{Tcs, EV_UNPARK, WAIT_INDEFINITE},
},
},
thread,
time::Instant,
};

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
parked: AtomicBool,
tcs: Tcs,
}

impl ThreadParker {
pub const IS_CHEAP_TO_CONSTRUCT: bool = true;

#[inline]
pub fn new() -> ThreadParker {
ThreadParker {
parked: AtomicBool::new(false),
tcs: current_tcs(),
}
}

// Prepares the parker. This should be called before adding it to the queue.
#[inline]
pub fn prepare_park(&self) {
self.parked.store(true, Ordering::Relaxed);
}

// Checks if the park timed out. This should be called while holding the
// queue lock after park_until has returned false.
#[inline]
pub fn timed_out(&self) -> bool {
self.parked.load(Ordering::Relaxed)
}

// Parks the thread until it is unparked. This should be called after it has
// been added to the queue, after unlocking the queue.
#[inline]
pub fn park(&self) {
while self.parked.load(Ordering::Acquire) {
let res = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).expect("wait returned error");
assert_eq!(res & EV_UNPARK, EV_UNPARK);
}
}

// Parks the thread until it is unparked or the timeout is reached. This
// should be called after it has been added to the queue, after unlocking
// the queue. Returns true if we were unparked and false if we timed out.
#[inline]
pub fn park_until(&self, timeout: Instant) -> bool {
while self.parked.load(Ordering::Acquire) {
if Instant::now() >= timeout {
return false;
}
// SGX current does not support waiting for an event with a
// timeout. So we just implement this as a busy spin loop.
spin_loop_hint();
}
true
}

// Locks the parker to prevent the target thread from exiting. This is
// necessary to ensure that thread-local ThreadData objects remain valid.
// This should be called while holding the queue lock.
#[inline]
pub fn unpark_lock(&self) -> UnparkHandle {
// We don't need to lock anything, just clear the state
self.parked.store(false, Ordering::Release);
UnparkHandle(self.tcs)
}
}

// Handle for a thread that is about to be unparked. We need to mark the thread
// as unparked while holding the queue lock, but we delay the actual unparking
// until after the queue lock is released.
pub struct UnparkHandle(Tcs);

impl UnparkHandle {
// Wakes up the parked thread. This should be called after the queue lock is
// released to avoid blocking the queue for too long.
#[inline]
pub fn unpark(self) {
usercalls::send(EV_UNPARK, Some(self.0)).expect("send returned error");
}
}

#[inline]
pub fn thread_yield() {
thread::yield_now();
}

0 comments on commit 7545f72

Please sign in to comment.