1- use crate :: num:: NonZeroUsize ;
21/// A simple queue implementation for synchronization primitives.
32///
43/// This queue is used to implement condition variable and mutexes.
@@ -10,7 +9,10 @@ use crate::num::NonZeroUsize;
109/// Since userspace may send spurious wake-ups, the wakeup event state is
1110/// recorded in the enclave. The wakeup event state is protected by a spinlock.
1211/// The queue and associated wait state are stored in a `WaitVariable`.
12+ use crate :: num:: NonZeroUsize ;
1313use crate :: ops:: { Deref , DerefMut } ;
14+ use crate :: sys:: wait_timeout_sgx;
15+ use crate :: time:: Duration ;
1416
1517use super :: abi:: thread;
1618use super :: abi:: usercalls;
@@ -158,6 +160,37 @@ impl WaitQueue {
158160 }
159161 }
160162
163+ /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
164+ /// until a wakeup event or timeout. If event was observed, returns true.
165+ /// If not, it will remove the calling thread from the wait queue.
166+ pub fn wait_timeout < T , F : FnOnce ( ) > (
167+ lock : & SpinMutex < WaitVariable < T > > ,
168+ timeout : Duration ,
169+ before_wait : F ,
170+ ) -> bool {
171+ // very unsafe: check requirements of UnsafeList::push
172+ unsafe {
173+ let mut entry = UnsafeListEntry :: new ( SpinMutex :: new ( WaitEntry {
174+ tcs : thread:: current ( ) ,
175+ wake : false ,
176+ } ) ) ;
177+ let entry_lock = lock. lock ( ) . queue . inner . push ( & mut entry) ;
178+ before_wait ( ) ;
179+ // don't panic, this would invalidate `entry` during unwinding
180+ wait_timeout_sgx ( EV_UNPARK , timeout) ;
181+ // acquire the wait queue's lock first to avoid deadlock.
182+ let mut guard = lock. lock ( ) ;
183+ let entry_guard = entry_lock. lock ( ) ;
184+ let success = entry_guard. wake ;
185+ if !success {
186+ // nobody is waking us up, so remove the entry from the wait queue.
187+ drop ( entry_guard) ;
188+ guard. queue . inner . remove ( & mut entry) ;
189+ }
190+ success
191+ }
192+ }
193+
161194 /// Either find the next waiter on the wait queue, or return the mutex
162195 /// guard unchanged.
163196 ///
@@ -325,6 +358,31 @@ mod unsafe_list {
325358 Some ( ( * first. as_ptr ( ) ) . value . as_ref ( ) . unwrap ( ) )
326359 }
327360 }
361+
362+ /// Removes an entry from the list.
363+ ///
364+ /// # Safety
365+ ///
366+ /// The caller must ensure that entry has been pushed prior to this
367+ /// call and has not moved since push.
368+ pub unsafe fn remove ( & mut self , entry : & mut UnsafeListEntry < T > ) {
369+ rtassert ! ( !self . is_empty( ) ) ;
370+ // BEFORE:
371+ // /----\ next ---> /-----\ next ---> /----\
372+ // ... |prev| |entry| |next| ...
373+ // \----/ <--- prev \-----/ <--- prev \----/
374+ //
375+ // AFTER:
376+ // /----\ next ---> /----\
377+ // ... |prev| |next| ...
378+ // \----/ <--- prev \----/
379+ let mut prev = entry. prev ;
380+ let mut next = entry. next ;
381+ prev. as_mut ( ) . next = next;
382+ next. as_mut ( ) . prev = prev;
383+ entry. next = NonNull :: dangling ( ) ;
384+ entry. prev = NonNull :: dangling ( ) ;
385+ }
328386 }
329387
330388 #[ cfg( test) ]
@@ -354,6 +412,51 @@ mod unsafe_list {
354412 }
355413 }
356414
415+ #[ test]
416+ fn push_remove ( ) {
417+ unsafe {
418+ let mut node = UnsafeListEntry :: new ( 1234 ) ;
419+ let mut list = UnsafeList :: new ( ) ;
420+ assert_eq ! ( list. push( & mut node) , & 1234 ) ;
421+ list. remove ( & mut node) ;
422+ assert_empty ( & mut list) ;
423+ }
424+ }
425+
426+ #[ test]
427+ fn push_remove_pop ( ) {
428+ unsafe {
429+ let mut node1 = UnsafeListEntry :: new ( 11 ) ;
430+ let mut node2 = UnsafeListEntry :: new ( 12 ) ;
431+ let mut node3 = UnsafeListEntry :: new ( 13 ) ;
432+ let mut node4 = UnsafeListEntry :: new ( 14 ) ;
433+ let mut node5 = UnsafeListEntry :: new ( 15 ) ;
434+ let mut list = UnsafeList :: new ( ) ;
435+ assert_eq ! ( list. push( & mut node1) , & 11 ) ;
436+ assert_eq ! ( list. push( & mut node2) , & 12 ) ;
437+ assert_eq ! ( list. push( & mut node3) , & 13 ) ;
438+ assert_eq ! ( list. push( & mut node4) , & 14 ) ;
439+ assert_eq ! ( list. push( & mut node5) , & 15 ) ;
440+
441+ list. remove ( & mut node1) ;
442+ assert_eq ! ( list. pop( ) . unwrap( ) , & 12 ) ;
443+ list. remove ( & mut node3) ;
444+ assert_eq ! ( list. pop( ) . unwrap( ) , & 14 ) ;
445+ list. remove ( & mut node5) ;
446+ assert_empty ( & mut list) ;
447+
448+ assert_eq ! ( list. push( & mut node1) , & 11 ) ;
449+ assert_eq ! ( list. pop( ) . unwrap( ) , & 11 ) ;
450+ assert_empty ( & mut list) ;
451+
452+ assert_eq ! ( list. push( & mut node3) , & 13 ) ;
453+ assert_eq ! ( list. push( & mut node4) , & 14 ) ;
454+ list. remove ( & mut node3) ;
455+ list. remove ( & mut node4) ;
456+ assert_empty ( & mut list) ;
457+ }
458+ }
459+
357460 #[ test]
358461 fn complex_pushes_pops ( ) {
359462 unsafe {
@@ -474,7 +577,7 @@ mod spin_mutex {
474577 use super :: * ;
475578 use crate :: sync:: Arc ;
476579 use crate :: thread;
477- use crate :: time:: { Duration , SystemTime } ;
580+ use crate :: time:: Duration ;
478581
479582 #[ test]
480583 fn sleep ( ) {
@@ -485,11 +588,7 @@ mod spin_mutex {
485588 * mutex2. lock ( ) = 1 ;
486589 } ) ;
487590
488- // "sleep" for 50ms
489- // FIXME: https://github.com/fortanix/rust-sgx/issues/31
490- let start = SystemTime :: now ( ) ;
491- let max = Duration :: from_millis ( 50 ) ;
492- while start. elapsed ( ) . unwrap ( ) < max { }
591+ thread:: sleep ( Duration :: from_millis ( 50 ) ) ;
493592
494593 assert_eq ! ( * guard, 0 ) ;
495594 drop ( guard) ;
0 commit comments