Skip to content

Commit

Permalink
Move usercall_wait_timeout to abi::usercalls::wait_timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
mzohreva committed Jul 15, 2020
1 parent 1466598 commit 85c25ae
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 82 deletions.
74 changes: 72 additions & 2 deletions src/libstd/sys/sgx/abi/usercalls/mod.rs
@@ -1,8 +1,8 @@
use crate::cmp;
use crate::convert::TryFrom;
use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult};
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
use crate::sys::rand::rdrand64;
use crate::time::Duration;
use crate::time::{Duration, Instant};

pub(crate) mod alloc;
#[macro_use]
Expand Down Expand Up @@ -169,6 +169,76 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
}

/// This function makes an effort to wait for a non-spurious event at least as
/// long as `duration`. Note that in general there is no guarantee about accuracy
/// of time and timeouts in SGX model. The enclave runner serving usercalls may
/// lie about current time and/or ignore timeout values.
///
/// Once the event is observed, `should_wake_up` will be used to determine
/// whether or not the event was spurious.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F)
where
F: Fn() -> bool,
{
// Calls the wait usercall and checks the result. Returns true if event was
// returned, and false if WouldBlock/TimedOut was returned.
// If duration is None, it will use WAIT_NO.
fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
let timeout = duration.map_or(raw::WAIT_NO, |duration| {
cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
});
match wait(event_mask, timeout) {
Ok(eventset) => {
if event_mask == 0 {
rtabort!("expected wait() to return Err, found Ok.");
}
rtassert!(eventset != 0 && eventset & !event_mask == 0);
true
}
Err(e) => {
rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
false
}
}
}

match wait_checked(event_mask, Some(duration)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}

// Drain all cached events.
// Note that `event_mask != 0` is implied if we get here.
loop {
match wait_checked(event_mask, None) {
false => break, // no more cached events
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
}

// Continue waiting, but take note of time spent waiting so we don't wait
// forever. We intentionally don't call `Instant::now()` before this point
// to avoid the cost of the `insecure_time` usercall in case there are no
// spurious wakeups.

let start = Instant::now();
let mut remaining = duration;
loop {
match wait_checked(event_mask, Some(remaining)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
remaining = match duration.checked_sub(start.elapsed()) {
Some(remaining) => remaining,
None => break,
}
}
}

/// Usercall `send`. See the ABI documentation for more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
Expand Down
76 changes: 0 additions & 76 deletions src/libstd/sys/sgx/mod.rs
Expand Up @@ -110,82 +110,6 @@ pub fn decode_error_kind(code: i32) -> ErrorKind {
}
}

// This function makes an effort to wait for a non-spurious event at least as
// long as `duration`. Note that in general there is no guarantee about accuracy
// of time and timeouts in SGX model. The enclave runner serving usercalls may
// lie about current time and/or ignore timeout values.
//
// Once the event is observed, `should_wake_up` will be used to determine
// whether or not the event was spurious.
pub fn usercall_wait_timeout<F>(event_mask: u64, duration: crate::time::Duration, should_wake_up: F)
where
F: Fn() -> bool,
{
use self::abi::usercalls;
use crate::cmp;
use crate::io::ErrorKind;
use crate::time::{Duration, Instant};

// Calls the wait usercall and checks the result. Returns true if event was
// returned, and false if WouldBlock/TimedOut was returned.
// If duration is None, it will use WAIT_NO.
fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
let timeout = duration.map_or(usercalls::raw::WAIT_NO, |duration| {
cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
});
match usercalls::wait(event_mask, timeout) {
Ok(eventset) => {
if event_mask == 0 {
rtabort!("expected usercalls::wait() to return Err, found Ok.");
}
// A matching event is one whose bits are equal to or a subset
// of `event_mask`.
rtassert!(eventset & !event_mask == 0);
true
}
Err(e) => {
rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
false
}
}
}

match wait_checked(event_mask, Some(duration)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}

// Drain all cached events.
// Note that `event_mask != 0` is implied if we get here.
loop {
match wait_checked(event_mask, None) {
false => break, // no more cached events
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
}

// Continue waiting, but take note of time spent waiting so we don't wait
// forever. We intentionally don't call `Instant::now()` before this point
// to avoid the cost of the `insecure_time` usercall in case there are no
// spurious wakeups.

let start = Instant::now();
let mut remaining = duration;
loop {
match wait_checked(event_mask, Some(remaining)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
remaining = match duration.checked_sub(start.elapsed()) {
Some(remaining) => remaining,
None => break,
}
}
}

// This enum is used as the storage for a bunch of types which can't actually
// exist.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
Expand Down
3 changes: 1 addition & 2 deletions src/libstd/sys/sgx/thread.rs
@@ -1,7 +1,6 @@
#![cfg_attr(test, allow(dead_code))] // why is this necessary?
use crate::ffi::CStr;
use crate::io;
use crate::sys::usercall_wait_timeout;
use crate::time::Duration;

use super::abi::usercalls;
Expand Down Expand Up @@ -75,7 +74,7 @@ impl Thread {
}

pub fn sleep(dur: Duration) {
usercall_wait_timeout(0, dur, || true);
usercalls::wait_timeout(0, dur, || true);
}

pub fn join(self) {
Expand Down
3 changes: 1 addition & 2 deletions src/libstd/sys/sgx/waitqueue.rs
Expand Up @@ -11,7 +11,6 @@
//! The queue and associated wait state are stored in a `WaitVariable`.
use crate::num::NonZeroUsize;
use crate::ops::{Deref, DerefMut};
use crate::sys::usercall_wait_timeout;
use crate::time::Duration;

use super::abi::thread;
Expand Down Expand Up @@ -176,7 +175,7 @@ impl WaitQueue {
}));
let entry_lock = lock.lock().queue.inner.push(&mut entry);
before_wait();
usercall_wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
// acquire the wait queue's lock first to avoid deadlock.
let mut guard = lock.lock();
let success = entry_lock.lock().wake;
Expand Down

0 comments on commit 85c25ae

Please sign in to comment.