diff --git a/.travis.yml b/.travis.yml index d8fa9cec..8523dc75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,9 @@ before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH +- if [ "${TRAVIS_RUST_VERSION}" = "nightly" ]; then + rustup target add x86_64-fortanix-unknown-sgx; + fi script: - cd core; @@ -22,6 +25,7 @@ script: - travis-cargo test - travis-cargo --only stable test -- --features=deadlock_detection - travis-cargo --only beta test -- --features=deadlock_detection +- travis-cargo --only nightly test -- --all --no-run --target x86_64-fortanix-unknown-sgx - travis-cargo --only nightly doc -- --all-features --no-deps -p parking_lot -p parking_lot_core -p lock_api - cd benchmark - travis-cargo build diff --git a/core/src/lib.rs b/core/src/lib.rs index d01846dd..dfef9d70 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,17 +39,28 @@ #![warn(missing_docs)] #![warn(rust_2018_idioms)] +#![cfg_attr( + all(target_env = "sgx", target_vendor = "fortanix"), + 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(target_env = "sgx", target_vendor = "fortanix"))] +#[path = "thread_parker/sgx.rs"] +mod thread_parker; + +#[cfg(not(any(windows, unix, all(target_env = "sgx", target_vendor = "fortanix"))))] #[path = "thread_parker/generic.rs"] mod thread_parker; diff --git a/core/src/thread_parker/sgx.rs b/core/src/thread_parker/sgx.rs new file mode 100644 index 00000000..c5ef6cbe --- /dev/null +++ b/core/src/thread_parker/sgx.rs @@ -0,0 +1,108 @@ +// Copyright 2016 Amanieu d'Antras +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use core::sync::atomic::{AtomicBool, Ordering}; +use std::{ + io, + 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 result = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE); + debug_assert_eq!(result.expect("wait returned error") & 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 { + // FIXME: https://github.com/fortanix/rust-sgx/issues/31 + panic!("timeout not supported in SGX"); + } + + // 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) { + let result = usercalls::send(EV_UNPARK, Some(self.0)); + if cfg!(debug_assertions) { + if let Err(error) = result { + // `InvalidInput` may be returned if the thread we send to has + // already been unparked and exited. + if error.kind() != io::ErrorKind::InvalidInput { + panic!("send returned an unexpected error: {:?}", error); + } + } + } + } +} + +#[inline] +pub fn thread_yield() { + thread::yield_now(); +} diff --git a/src/rwlock.rs b/src/rwlock.rs index 03abe8b7..38eec996 100644 --- a/src/rwlock.rs +++ b/src/rwlock.rs @@ -533,7 +533,15 @@ mod tests { thread::spawn(move || { let _lock = arc2.write(); }); - thread::sleep(Duration::from_millis(100)); + + if cfg!(not(all(target_env = "sgx", target_vendor = "fortanix"))) { + thread::sleep(Duration::from_millis(100)); + } else { + // FIXME: https://github.com/fortanix/rust-sgx/issues/31 + for _ in 0..100 { + thread::yield_now(); + } + } // A normal read would block here since there is a pending writer let _lock2 = arc.read_recursive();