|
1 | 1 | use crate::cmp;
|
2 | 2 | use crate::convert::TryFrom;
|
3 |
| -use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult}; |
| 3 | +use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; |
4 | 4 | use crate::sys::rand::rdrand64;
|
5 |
| -use crate::time::Duration; |
| 5 | +use crate::time::{Duration, Instant}; |
6 | 6 |
|
7 | 7 | pub(crate) mod alloc;
|
8 | 8 | #[macro_use]
|
@@ -169,6 +169,76 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
|
169 | 169 | unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
|
170 | 170 | }
|
171 | 171 |
|
| 172 | +/// This function makes an effort to wait for a non-spurious event at least as |
| 173 | +/// long as `duration`. Note that in general there is no guarantee about accuracy |
| 174 | +/// of time and timeouts in SGX model. The enclave runner serving usercalls may |
| 175 | +/// lie about current time and/or ignore timeout values. |
| 176 | +/// |
| 177 | +/// Once the event is observed, `should_wake_up` will be used to determine |
| 178 | +/// whether or not the event was spurious. |
| 179 | +#[unstable(feature = "sgx_platform", issue = "56975")] |
| 180 | +pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F) |
| 181 | +where |
| 182 | + F: Fn() -> bool, |
| 183 | +{ |
| 184 | + // Calls the wait usercall and checks the result. Returns true if event was |
| 185 | + // returned, and false if WouldBlock/TimedOut was returned. |
| 186 | + // If duration is None, it will use WAIT_NO. |
| 187 | + fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool { |
| 188 | + let timeout = duration.map_or(raw::WAIT_NO, |duration| { |
| 189 | + cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 |
| 190 | + }); |
| 191 | + match wait(event_mask, timeout) { |
| 192 | + Ok(eventset) => { |
| 193 | + if event_mask == 0 { |
| 194 | + rtabort!("expected wait() to return Err, found Ok."); |
| 195 | + } |
| 196 | + rtassert!(eventset != 0 && eventset & !event_mask == 0); |
| 197 | + true |
| 198 | + } |
| 199 | + Err(e) => { |
| 200 | + rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); |
| 201 | + false |
| 202 | + } |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + match wait_checked(event_mask, Some(duration)) { |
| 207 | + false => return, // timed out |
| 208 | + true if should_wake_up() => return, // woken up |
| 209 | + true => {} // spurious event |
| 210 | + } |
| 211 | + |
| 212 | + // Drain all cached events. |
| 213 | + // Note that `event_mask != 0` is implied if we get here. |
| 214 | + loop { |
| 215 | + match wait_checked(event_mask, None) { |
| 216 | + false => break, // no more cached events |
| 217 | + true if should_wake_up() => return, // woken up |
| 218 | + true => {} // spurious event |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + // Continue waiting, but take note of time spent waiting so we don't wait |
| 223 | + // forever. We intentionally don't call `Instant::now()` before this point |
| 224 | + // to avoid the cost of the `insecure_time` usercall in case there are no |
| 225 | + // spurious wakeups. |
| 226 | + |
| 227 | + let start = Instant::now(); |
| 228 | + let mut remaining = duration; |
| 229 | + loop { |
| 230 | + match wait_checked(event_mask, Some(remaining)) { |
| 231 | + false => return, // timed out |
| 232 | + true if should_wake_up() => return, // woken up |
| 233 | + true => {} // spurious event |
| 234 | + } |
| 235 | + remaining = match duration.checked_sub(start.elapsed()) { |
| 236 | + Some(remaining) => remaining, |
| 237 | + None => break, |
| 238 | + } |
| 239 | + } |
| 240 | +} |
| 241 | + |
172 | 242 | /// Usercall `send`. See the ABI documentation for more information.
|
173 | 243 | #[unstable(feature = "sgx_platform", issue = "56975")]
|
174 | 244 | pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
|
|
0 commit comments