@@ -128,7 +128,7 @@ struct Condvar {
128128/// The futex state.
129129#[ derive( Default , Debug ) ]
130130struct Futex {
131- waiters : VecDeque < FutexWaiter > ,
131+ waiters : Vec < FutexWaiter > ,
132132 /// Tracks the happens-before relationship
133133 /// between a futex-wake and a futex-wait
134134 /// during a non-spurious wake event.
@@ -140,6 +140,12 @@ struct Futex {
140140#[ derive( Default , Clone ) ]
141141pub struct FutexRef ( Rc < RefCell < Futex > > ) ;
142142
143+ impl FutexRef {
144+ pub fn waiters ( & self ) -> usize {
145+ self . 0 . borrow ( ) . waiters . len ( )
146+ }
147+ }
148+
143149impl VisitProvenance for FutexRef {
144150 fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
145151 // No provenance in `Futex`.
@@ -728,25 +734,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728734 interp_ok ( true )
729735 }
730736
731- /// Wait for the futex to be signaled, or a timeout.
732- /// On a signal, `retval_succ` is written to `dest`.
733- /// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
737+ /// Wait for the futex to be signaled, or a timeout. Once the thread is
738+ /// unblocked, `callback` is called with the unblock reason.
734739 fn futex_wait (
735740 & mut self ,
736741 futex_ref : FutexRef ,
737742 bitset : u32 ,
738743 timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
739- retval_succ : Scalar ,
740- retval_timeout : Scalar ,
741- dest : MPlaceTy < ' tcx > ,
742- errno_timeout : IoError ,
744+ callback : DynUnblockCallback < ' tcx > ,
743745 ) {
744746 let this = self . eval_context_mut ( ) ;
745747 let thread = this. active_thread ( ) ;
746748 let mut futex = futex_ref. 0 . borrow_mut ( ) ;
747749 let waiters = & mut futex. waiters ;
748750 assert ! ( waiters. iter( ) . all( |waiter| waiter. thread != thread) , "thread is already waiting" ) ;
749- waiters. push_back ( FutexWaiter { thread, bitset } ) ;
751+ waiters. push ( FutexWaiter { thread, bitset } ) ;
750752 drop ( futex) ;
751753
752754 this. block_thread (
@@ -755,10 +757,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
755757 callback ! (
756758 @capture<' tcx> {
757759 futex_ref: FutexRef ,
758- retval_succ: Scalar ,
759- retval_timeout: Scalar ,
760- dest: MPlaceTy <' tcx>,
761- errno_timeout: IoError ,
760+ callback: DynUnblockCallback <' tcx>,
762761 }
763762 |this, unblock: UnblockKind | {
764763 match unblock {
@@ -768,29 +767,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
768767 if let Some ( data_race) = & this. machine. data_race {
769768 data_race. acquire_clock( & futex. clock, & this. machine. threads) ;
770769 }
771- // Write the return value.
772- this. write_scalar( retval_succ, & dest) ?;
773- interp_ok( ( ) )
774770 } ,
775771 UnblockKind :: TimedOut => {
776772 // Remove the waiter from the futex.
777773 let thread = this. active_thread( ) ;
778774 let mut futex = futex_ref. 0 . borrow_mut( ) ;
779775 futex. waiters. retain( |waiter| waiter. thread != thread) ;
780- // Set errno and write return value.
781- this. set_last_error( errno_timeout) ?;
782- this. write_scalar( retval_timeout, & dest) ?;
783- interp_ok( ( ) )
784776 } ,
785777 }
778+
779+ callback. call( this, unblock)
786780 }
787781 ) ,
788782 ) ;
789783 }
790784
791- /// Wake up the first thread in the queue that matches any of the bits in the bitset.
792- /// Returns whether anything was woken.
793- fn futex_wake ( & mut self , futex_ref : & FutexRef , bitset : u32 ) -> InterpResult < ' tcx , bool > {
785+ /// Wake up `count` of the threads in the queue that match any of the bits
786+ /// in the bitset. Returns how many threads were woken.
787+ fn futex_wake (
788+ & mut self ,
789+ futex_ref : & FutexRef ,
790+ bitset : u32 ,
791+ count : usize ,
792+ ) -> InterpResult < ' tcx , usize > {
794793 let this = self . eval_context_mut ( ) ;
795794 let mut futex = futex_ref. 0 . borrow_mut ( ) ;
796795 let data_race = & this. machine . data_race ;
@@ -800,13 +799,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
800799 data_race. release_clock ( & this. machine . threads , |clock| futex. clock . clone_from ( clock) ) ;
801800 }
802801
803- // Wake up the first thread in the queue that matches any of the bits in the bitset.
804- let Some ( i ) = futex . waiters . iter ( ) . position ( |w| w . bitset & bitset != 0 ) else {
805- return interp_ok ( false ) ;
806- } ;
807- let waiter = futex. waiters . remove ( i ) . unwrap ( ) ;
802+ // Remove `count` of the threads in the queue that match any of the bits in the bitset.
803+ // We collect all of them before unblocking because the unblock callback may access the
804+ // futex state to retrieve the remaining number of waiters on macOS.
805+ let waiters : Vec < _ > =
806+ futex. waiters . extract_if ( .. , |w| w . bitset & bitset != 0 ) . take ( count ) . collect ( ) ;
808807 drop ( futex) ;
809- this. unblock_thread ( waiter. thread , BlockReason :: Futex ) ?;
810- interp_ok ( true )
808+
809+ let woken = waiters. len ( ) ;
810+ for waiter in waiters {
811+ this. unblock_thread ( waiter. thread , BlockReason :: Futex ) ?;
812+ }
813+
814+ interp_ok ( woken)
811815 }
812816}
0 commit comments