1
+ use std:: cell:: RefCell ;
1
2
use std:: collections:: VecDeque ;
2
3
use std:: collections:: hash_map:: Entry ;
3
4
use std:: ops:: Not ;
5
+ use std:: rc:: Rc ;
4
6
use std:: time:: Duration ;
5
7
6
8
use rustc_abi:: Size ;
@@ -121,6 +123,15 @@ struct Futex {
121
123
clock : VClock ,
122
124
}
123
125
126
+ #[ derive( Default , Clone ) ]
127
+ pub struct FutexRef ( Rc < RefCell < Futex > > ) ;
128
+
129
+ impl VisitProvenance for FutexRef {
130
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
131
+ // No provenance
132
+ }
133
+ }
134
+
124
135
/// A thread waiting on a futex.
125
136
#[ derive( Debug ) ]
126
137
struct FutexWaiter {
@@ -137,9 +148,6 @@ pub struct SynchronizationObjects {
137
148
rwlocks : IndexVec < RwLockId , RwLock > ,
138
149
condvars : IndexVec < CondvarId , Condvar > ,
139
150
pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
140
-
141
- /// Futex info for the futex at the given address.
142
- futexes : FxHashMap < u64 , Futex > ,
143
151
}
144
152
145
153
// Private extension trait for local helper methods
@@ -184,7 +192,7 @@ impl SynchronizationObjects {
184
192
}
185
193
186
194
impl < ' tcx > AllocExtra < ' tcx > {
187
- pub fn get_sync < T : ' static > ( & self , offset : Size ) -> Option < & T > {
195
+ fn get_sync < T : ' static > ( & self , offset : Size ) -> Option < & T > {
188
196
self . sync . get ( & offset) . and_then ( |s| s. downcast_ref :: < T > ( ) )
189
197
}
190
198
}
@@ -286,7 +294,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
286
294
// is truly the only place where the data could be stored.
287
295
this. check_ptr_access ( ptr, Size :: from_bytes ( 1 ) , CheckInAllocMsg :: InboundsTest ) ?;
288
296
289
- let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) ? ;
297
+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) . unwrap ( ) ;
290
298
let ( alloc_extra, machine) = this. get_alloc_extra_mut ( alloc) ?;
291
299
// Due to borrow checker reasons, we have to do the lookup twice.
292
300
if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
@@ -296,6 +304,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
296
304
interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
297
305
}
298
306
307
+ /// If the pointer is inbounds of an allocation, get the synchronization primitive associated
308
+ /// with the given pointer, or initialize a new one.
309
+ ///
310
+ /// Otherwise, return `None`.
311
+ fn try_get_sync_or_init < ' a , T : ' static > (
312
+ & ' a mut self ,
313
+ ptr : Pointer ,
314
+ new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> InterpResult < ' tcx , T > ,
315
+ ) -> Option < & ' a T >
316
+ where
317
+ ' tcx : ' a ,
318
+ {
319
+ let this = self . eval_context_mut ( ) ;
320
+ if this. ptr_try_get_alloc_id ( ptr, 0 ) . ok ( ) . is_some_and ( |( alloc_id, offset, ..) | {
321
+ let ( size, _align, kind) = this. get_alloc_info ( alloc_id) ;
322
+ // FIXME: we should also check mutability
323
+ kind == AllocKind :: LiveData && offset < size
324
+ } ) {
325
+ // This cannot fail now.
326
+ Some ( this. get_sync_or_init ( ptr, new) . unwrap ( ) )
327
+ } else {
328
+ None
329
+ }
330
+ }
331
+
299
332
#[ inline]
300
333
/// Get the id of the thread that currently owns this lock.
301
334
fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
@@ -690,7 +723,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
690
723
/// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
691
724
fn futex_wait (
692
725
& mut self ,
693
- addr : u64 ,
726
+ futex_ref : FutexRef ,
694
727
bitset : u32 ,
695
728
timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
696
729
retval_succ : Scalar ,
@@ -700,23 +733,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
700
733
) {
701
734
let this = self . eval_context_mut ( ) ;
702
735
let thread = this. active_thread ( ) ;
703
- let futex = & mut this . machine . sync . futexes . entry ( addr ) . or_default ( ) ;
736
+ let mut futex = futex_ref . 0 . borrow_mut ( ) ;
704
737
let waiters = & mut futex. waiters ;
705
738
assert ! ( waiters. iter( ) . all( |waiter| waiter. thread != thread) , "thread is already waiting" ) ;
706
739
waiters. push_back ( FutexWaiter { thread, bitset } ) ;
740
+ drop ( futex) ;
741
+
707
742
this. block_thread (
708
- BlockReason :: Futex { addr } ,
743
+ BlockReason :: Futex ,
709
744
timeout,
710
745
callback ! (
711
746
@capture<' tcx> {
712
- addr : u64 ,
747
+ futex_ref : FutexRef ,
713
748
retval_succ: Scalar ,
714
749
retval_timeout: Scalar ,
715
750
dest: MPlaceTy <' tcx>,
716
751
errno_timeout: IoError ,
717
752
}
718
753
@unblock = |this| {
719
- let futex = this . machine . sync . futexes . get ( & addr ) . unwrap ( ) ;
754
+ let futex = futex_ref . 0 . borrow ( ) ;
720
755
// Acquire the clock of the futex.
721
756
if let Some ( data_race) = & this. machine. data_race {
722
757
data_race. acquire_clock( & futex. clock, & this. machine. threads) ;
@@ -728,7 +763,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728
763
@timeout = |this| {
729
764
// Remove the waiter from the futex.
730
765
let thread = this. active_thread( ) ;
731
- let futex = this . machine . sync . futexes . get_mut ( & addr ) . unwrap ( ) ;
766
+ let mut futex = futex_ref . 0 . borrow_mut ( ) ;
732
767
futex. waiters. retain( |waiter| waiter. thread != thread) ;
733
768
// Set errno and write return value.
734
769
this. set_last_error( errno_timeout) ?;
@@ -739,12 +774,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
739
774
) ;
740
775
}
741
776
777
+ /// Wake up the first thread in the queue that matches any of the bits in the bitset.
742
778
/// Returns whether anything was woken.
743
- fn futex_wake ( & mut self , addr : u64 , bitset : u32 ) -> InterpResult < ' tcx , bool > {
779
+ fn futex_wake ( & mut self , futex_ref : & FutexRef , bitset : u32 ) -> InterpResult < ' tcx , bool > {
744
780
let this = self . eval_context_mut ( ) ;
745
- let Some ( futex) = this. machine . sync . futexes . get_mut ( & addr) else {
746
- return interp_ok ( false ) ;
747
- } ;
781
+ let mut futex = futex_ref. 0 . borrow_mut ( ) ;
748
782
let data_race = & this. machine . data_race ;
749
783
750
784
// Each futex-wake happens-before the end of the futex wait
@@ -757,7 +791,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
757
791
return interp_ok ( false ) ;
758
792
} ;
759
793
let waiter = futex. waiters . remove ( i) . unwrap ( ) ;
760
- this. unblock_thread ( waiter. thread , BlockReason :: Futex { addr } ) ?;
794
+ drop ( futex) ;
795
+ this. unblock_thread ( waiter. thread , BlockReason :: Futex ) ?;
761
796
interp_ok ( true )
762
797
}
763
798
}
0 commit comments