Skip to content

Commit

Permalink
Merge #252
Browse files Browse the repository at this point in the history
252: Allow arbitrary timeouts for wait usercall r=jethrogb a=mzohreva

This should partially address issue #31

Co-authored-by: Mohsen Zohrevandi <mohsen.zohrevandi@fortanix.com>
  • Loading branch information
bors[bot] and mzohreva committed Jul 20, 2020
2 parents 981505f + 808cecc commit 4de2fd6
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 21 deletions.
48 changes: 33 additions & 15 deletions enclave-runner/src/usercalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::pin::Pin;

use std::sync::Arc;
use std::thread;
use std::time;
use std::time::{self, Duration, Instant};
use std::task::{Poll, Context, Waker};

use failure;
Expand Down Expand Up @@ -1316,14 +1316,20 @@ impl<'tcs> IOHandlerInput<'tcs> {

#[inline(always)]
async fn wait(&mut self, event_mask: u64, timeout: u64) -> IoResult<u64> {
let wait = match timeout {
WAIT_NO => false,
WAIT_INDEFINITE => true,
_ => return Err(IoErrorKind::InvalidInput.into()),
};

let event_mask = Self::check_event_set(event_mask)?;

let timeout = match timeout {
WAIT_NO | WAIT_INDEFINITE => timeout,
_ => {
// tokio::time::timeout does not handle large timeout values
// well which may or may not be a bug in tokio, but it seems to
// work ok with timeout values smaller than 2 ^ 55 nanoseconds.
// NOTE: 2 ^ 55 nanoseconds is roughly 417 days.
// TODO: revisit when https://github.com/tokio-rs/tokio/issues/2667 is resolved.
cmp::min(1 << 55, timeout)
}
};

let mut ret = None;

if (self.tcs.pending_event_set & event_mask) != 0 {
Expand All @@ -1339,17 +1345,29 @@ impl<'tcs> IOHandlerInput<'tcs> {
}

if ret.is_none() {
let start = Instant::now();
loop {
let ev = if wait {
Ok(self.tcs.event_queue.next().await.unwrap())
} else {
match self.tcs.event_queue.try_next() {
let ev = match timeout {
WAIT_INDEFINITE => self.tcs.event_queue.next().await.ok_or(()),
WAIT_NO => match self.tcs.event_queue.try_next() {
Ok(Some(ev)) => Ok(ev),
Err(_) => break,
Ok(None) => Err(()),
Err(_) => break,
},
timeout => {
let remaining = match Duration::from_nanos(timeout).checked_sub(start.elapsed()) {
None => break,
Some(ref duration) if duration.as_nanos() == 0 => break,
Some(duration) => duration,
};
match tokio::time::timeout(remaining, self.tcs.event_queue.next()).await {
Ok(Some(ev)) => Ok(ev),
Ok(None) => Err(()),
Err(_) => break, // timed out
}
}
}
.expect("TCS event queue disconnected unexpectedly");
}.expect("TCS event queue disconnected unexpectedly");

if (ev & (EV_ABORT as u8)) != 0 {
// dispatch will make sure this is not returned to enclave
return Err(IoErrorKind::Other.into());
Expand All @@ -1368,7 +1386,7 @@ impl<'tcs> IOHandlerInput<'tcs> {
if let Some(ret) = ret {
Ok(ret.into())
} else {
Err(IoErrorKind::WouldBlock.into())
Err(if timeout == WAIT_NO { IoErrorKind::WouldBlock } else { IoErrorKind::TimedOut }.into())
}
}

Expand Down
18 changes: 12 additions & 6 deletions fortanix-sgx-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,9 +464,10 @@ pub const WAIT_INDEFINITE: u64 = !0;
/// ## TCS event queues
///
/// Userspace will maintain a queue for each running TCS with events to be
/// delivered. Each event is characterized by a bitset. Userspace or the
/// enclave (using the `send` usercall) can put events on this queue. If the
/// enclave isn't waiting for an event when an event is queued, the event
/// delivered. Each event is characterized by a bitset with at least one bit
/// set. Userspace or the enclave (using the `send` usercall) can put events on
/// this queue.
/// If the enclave isn't waiting for an event when an event is queued, the event
/// remains on the queue until it delivered to the enclave in a later `wait`
/// usercall. If an enclave is waiting for an event, and the queue contains an
/// event that is a subset of the waited-for event mask, that event is removed
Expand Down Expand Up @@ -503,14 +504,18 @@ impl Usercalls {

/// Wait for an event to occur, or check if an event is currently pending.
///
/// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`]. If it is another
/// value, userspace will return an error.
/// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`] or a positive
/// value smaller than u64::MAX specifying number of nanoseconds to wait.
///
/// If `timeout` is [`WAIT_INDEFINITE`], this call will block and return
/// once a matching event is queued on this TCS. If `timeout` is
/// [`WAIT_NO`], this call will return immediately, and the return value
/// will indicate if an event was pending. If it was, it has been dequeued.
/// If not, the [`WouldBlock`] error value will be returned.
/// If not, the [`WouldBlock`] error value will be returned. If `timeout`
/// is a value other than [`WAIT_NO`] and [`WAIT_INDEFINITE`], this call
/// will block until either a matching event is queued in which case the
/// return value will indicate the event, or the timeout is reached in
/// which case the [`TimedOut`] error value will be returned.
///
/// A matching event is one whose bits are equal to or a subset of
/// `event_mask`. If `event_mask` is `0`, this call will never return due
Expand All @@ -534,6 +539,7 @@ impl Usercalls {
/// [`WAIT_INDEFINITE`]: constant.WAIT_INDEFINITE.html
/// [`EV_RETURNQ_NOT_EMPTY`]: constant.EV_RETURNQ_NOT_EMPTY.html
/// [`WouldBlock`]: enum.Error.html#variant.WouldBlock
/// [`TimedOut`]: enum.Error.html#variant.TimedOut
pub fn wait(event_mask: u64, timeout: u64) -> (Result, u64) { unimplemented!() }

/// Send an event to one or all TCSes.
Expand Down

0 comments on commit 4de2fd6

Please sign in to comment.