@@ -110,43 +110,76 @@ pub fn decode_error_kind(code: i32) -> ErrorKind {
110110 }
111111}
112112
113- // This function makes an effort to sleep at least as long as `duration`.
114- // Note that in general there is no guarantee about accuracy of time and
115- // timeouts in SGX model. The enclave runner serving usercalls may lie about
116- // current time and/or ignore timeout values.
113+ // This function makes an effort to wait for a non-spurious event at least as
114+ // long as `duration`. Note that in general there is no guarantee about accuracy
115+ // of time and timeouts in SGX model. The enclave runner serving usercalls may
116+ // lie about current time and/or ignore timeout values.
117117//
118- // Once the event is observed, `stop ` will be used to determine whether or not
119- // we should continue to wait .
118+ // Once the event is observed, `woken_up ` will be used to determine whether or
119+ // not the event was spurious .
120120//
121121// FIXME: note these caveats in documentation of all public types that use this
122122// function in their execution path.
123- pub fn wait_timeout_sgx < F > ( event_mask : u64 , duration : crate :: time:: Duration , stop : F )
123+ pub fn wait_timeout_sgx < F > ( event_mask : u64 , duration : crate :: time:: Duration , woken_up : F )
124124where
125125 F : Fn ( ) -> bool ,
126126{
127127 use self :: abi:: usercalls;
128128 use crate :: cmp;
129129 use crate :: io:: ErrorKind ;
130- use crate :: time:: Instant ;
131-
132- let start = Instant :: now ( ) ;
133- let mut remaining = duration;
134- loop {
135- let timeout = cmp:: min ( ( u64:: MAX - 1 ) as u128 , remaining. as_nanos ( ) ) as u64 ;
130+ use crate :: time:: { Duration , Instant } ;
131+
132+ // Calls the wait usercall and checks the result. Returns true if event was
133+ // returned, and false if WouldBlock/TimedOut was returned.
134+ // If duration is None, it will use WAIT_NO.
135+ fn wait_checked ( event_mask : u64 , duration : Option < Duration > ) -> bool {
136+ let timeout = duration. map_or ( usercalls:: raw:: WAIT_NO , |duration| {
137+ cmp:: min ( ( u64:: MAX - 1 ) as u128 , duration. as_nanos ( ) ) as u64
138+ } ) ;
136139 match usercalls:: wait ( event_mask, timeout) {
137140 Ok ( eventset) => {
138141 if event_mask == 0 {
139142 rtabort ! ( "expected usercalls::wait() to return Err, found Ok." ) ;
140143 }
141144 rtassert ! ( eventset & event_mask == event_mask) ;
142- if stop ( ) {
143- return ;
144- }
145+ true
145146 }
146147 Err ( e) => {
147- rtassert ! ( e. kind( ) == ErrorKind :: TimedOut || e. kind( ) == ErrorKind :: WouldBlock )
148+ rtassert ! ( e. kind( ) == ErrorKind :: TimedOut || e. kind( ) == ErrorKind :: WouldBlock ) ;
149+ false
148150 }
149151 }
152+ }
153+
154+ match wait_checked ( event_mask, Some ( duration) ) {
155+ false => return , // timed out
156+ true if woken_up ( ) => return , // woken up
157+ true => { } // spurious event
158+ }
159+
160+ // Drain all cached events.
161+ // Note that `event_mask != 0` is implied if we get here.
162+ loop {
163+ match wait_checked ( event_mask, None ) {
164+ false => break , // no more cached events
165+ true if woken_up ( ) => return , // woken up
166+ true => { } // spurious event
167+ }
168+ }
169+
170+ // Continue waiting, but take note of time spent waiting so we don't wait
171+ // forever. We intentionally don't call `Instant::now()` before this point
172+ // to avoid the cost of the `insecure_time` usercall in case there are no
173+ // spurious wakeups.
174+
175+ let start = Instant :: now ( ) ;
176+ let mut remaining = duration;
177+ loop {
178+ match wait_checked ( event_mask, Some ( remaining) ) {
179+ false => return , // timed out
180+ true if woken_up ( ) => return , // woken up
181+ true => { } // spurious event
182+ }
150183 remaining = match duration. checked_sub ( start. elapsed ( ) ) {
151184 Some ( remaining) => remaining,
152185 None => break ,
0 commit comments