@@ -128,7 +128,7 @@ struct Condvar {
128
128
/// The futex state.
129
129
#[ derive( Default , Debug ) ]
130
130
struct Futex {
131
- waiters : VecDeque < FutexWaiter > ,
131
+ waiters : Vec < FutexWaiter > ,
132
132
/// Tracks the happens-before relationship
133
133
/// between a futex-wake and a futex-wait
134
134
/// during a non-spurious wake event.
@@ -140,6 +140,12 @@ struct Futex {
140
140
#[ derive( Default , Clone ) ]
141
141
pub struct FutexRef ( Rc < RefCell < Futex > > ) ;
142
142
143
+ impl FutexRef {
144
+ pub fn waiters ( & self ) -> usize {
145
+ self . 0 . borrow ( ) . waiters . len ( )
146
+ }
147
+ }
148
+
143
149
impl VisitProvenance for FutexRef {
144
150
fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
145
151
// No provenance in `Futex`.
@@ -728,25 +734,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728
734
interp_ok ( true )
729
735
}
730
736
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.
734
739
fn futex_wait (
735
740
& mut self ,
736
741
futex_ref : FutexRef ,
737
742
bitset : u32 ,
738
743
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 > ,
743
745
) {
744
746
let this = self . eval_context_mut ( ) ;
745
747
let thread = this. active_thread ( ) ;
746
748
let mut futex = futex_ref. 0 . borrow_mut ( ) ;
747
749
let waiters = & mut futex. waiters ;
748
750
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 } ) ;
750
752
drop ( futex) ;
751
753
752
754
this. block_thread (
@@ -755,10 +757,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
755
757
callback ! (
756
758
@capture<' tcx> {
757
759
futex_ref: FutexRef ,
758
- retval_succ: Scalar ,
759
- retval_timeout: Scalar ,
760
- dest: MPlaceTy <' tcx>,
761
- errno_timeout: IoError ,
760
+ callback: DynUnblockCallback <' tcx>,
762
761
}
763
762
|this, unblock: UnblockKind | {
764
763
match unblock {
@@ -768,29 +767,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
768
767
if let Some ( data_race) = & this. machine. data_race {
769
768
data_race. acquire_clock( & futex. clock, & this. machine. threads) ;
770
769
}
771
- // Write the return value.
772
- this. write_scalar( retval_succ, & dest) ?;
773
- interp_ok( ( ) )
774
770
} ,
775
771
UnblockKind :: TimedOut => {
776
772
// Remove the waiter from the futex.
777
773
let thread = this. active_thread( ) ;
778
774
let mut futex = futex_ref. 0 . borrow_mut( ) ;
779
775
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( ( ) )
784
776
} ,
785
777
}
778
+
779
+ callback. call( this, unblock)
786
780
}
787
781
) ,
788
782
) ;
789
783
}
790
784
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 > {
794
793
let this = self . eval_context_mut ( ) ;
795
794
let mut futex = futex_ref. 0 . borrow_mut ( ) ;
796
795
let data_race = & this. machine . data_race ;
@@ -800,13 +799,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
800
799
data_race. release_clock ( & this. machine . threads , |clock| futex. clock . clone_from ( clock) ) ;
801
800
}
802
801
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 ( ) ;
808
807
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)
811
815
}
812
816
}
0 commit comments