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 in `Futex`.
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
}
@@ -273,27 +281,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
273
281
274
282
/// Get the synchronization primitive associated with the given pointer,
275
283
/// or initialize a new one.
284
+ ///
285
+ /// Return `None` if this pointer does not point to at least 1 byte of mutable memory.
276
286
fn get_sync_or_init < ' a , T : ' static > (
277
287
& ' a mut self ,
278
288
ptr : Pointer ,
279
- new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> InterpResult < ' tcx , T > ,
280
- ) -> InterpResult < ' tcx , & ' a T >
289
+ new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> T ,
290
+ ) -> Option < & ' a T >
281
291
where
282
292
' tcx : ' a ,
283
293
{
284
294
let this = self . eval_context_mut ( ) ;
285
- // Ensure there is memory behind this pointer, so that this allocation
286
- // is truly the only place where the data could be stored.
287
- this. check_ptr_access ( ptr, Size :: from_bytes ( 1 ) , CheckInAllocMsg :: InboundsTest ) ?;
288
-
289
- let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) ?;
290
- let ( alloc_extra, machine) = this. get_alloc_extra_mut ( alloc) ?;
295
+ if !this. ptr_try_get_alloc_id ( ptr, 0 ) . ok ( ) . is_some_and ( |( alloc_id, offset, ..) | {
296
+ let info = this. get_alloc_info ( alloc_id) ;
297
+ info. kind == AllocKind :: LiveData && info. mutbl . is_mut ( ) && offset < info. size
298
+ } ) {
299
+ return None ;
300
+ }
301
+ // This cannot fail now.
302
+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) . unwrap ( ) ;
303
+ let ( alloc_extra, machine) = this. get_alloc_extra_mut ( alloc) . unwrap ( ) ;
291
304
// Due to borrow checker reasons, we have to do the lookup twice.
292
305
if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
293
- let new = new ( machine) ? ;
306
+ let new = new ( machine) ;
294
307
alloc_extra. sync . insert ( offset, Box :: new ( new) ) ;
295
308
}
296
- interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
309
+ Some ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
297
310
}
298
311
299
312
#[ inline]
@@ -690,7 +703,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
690
703
/// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
691
704
fn futex_wait (
692
705
& mut self ,
693
- addr : u64 ,
706
+ futex_ref : FutexRef ,
694
707
bitset : u32 ,
695
708
timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
696
709
retval_succ : Scalar ,
@@ -700,23 +713,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
700
713
) {
701
714
let this = self . eval_context_mut ( ) ;
702
715
let thread = this. active_thread ( ) ;
703
- let futex = & mut this . machine . sync . futexes . entry ( addr ) . or_default ( ) ;
716
+ let mut futex = futex_ref . 0 . borrow_mut ( ) ;
704
717
let waiters = & mut futex. waiters ;
705
718
assert ! ( waiters. iter( ) . all( |waiter| waiter. thread != thread) , "thread is already waiting" ) ;
706
719
waiters. push_back ( FutexWaiter { thread, bitset } ) ;
720
+ drop ( futex) ;
721
+
707
722
this. block_thread (
708
- BlockReason :: Futex { addr } ,
723
+ BlockReason :: Futex ,
709
724
timeout,
710
725
callback ! (
711
726
@capture<' tcx> {
712
- addr : u64 ,
727
+ futex_ref : FutexRef ,
713
728
retval_succ: Scalar ,
714
729
retval_timeout: Scalar ,
715
730
dest: MPlaceTy <' tcx>,
716
731
errno_timeout: IoError ,
717
732
}
718
733
@unblock = |this| {
719
- let futex = this . machine . sync . futexes . get ( & addr ) . unwrap ( ) ;
734
+ let futex = futex_ref . 0 . borrow ( ) ;
720
735
// Acquire the clock of the futex.
721
736
if let Some ( data_race) = & this. machine. data_race {
722
737
data_race. acquire_clock( & futex. clock, & this. machine. threads) ;
@@ -728,7 +743,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728
743
@timeout = |this| {
729
744
// Remove the waiter from the futex.
730
745
let thread = this. active_thread( ) ;
731
- let futex = this . machine . sync . futexes . get_mut ( & addr ) . unwrap ( ) ;
746
+ let mut futex = futex_ref . 0 . borrow_mut ( ) ;
732
747
futex. waiters. retain( |waiter| waiter. thread != thread) ;
733
748
// Set errno and write return value.
734
749
this. set_last_error( errno_timeout) ?;
@@ -739,12 +754,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
739
754
) ;
740
755
}
741
756
757
+ /// Wake up the first thread in the queue that matches any of the bits in the bitset.
742
758
/// Returns whether anything was woken.
743
- fn futex_wake ( & mut self , addr : u64 , bitset : u32 ) -> InterpResult < ' tcx , bool > {
759
+ fn futex_wake ( & mut self , futex_ref : & FutexRef , bitset : u32 ) -> InterpResult < ' tcx , bool > {
744
760
let this = self . eval_context_mut ( ) ;
745
- let Some ( futex) = this. machine . sync . futexes . get_mut ( & addr) else {
746
- return interp_ok ( false ) ;
747
- } ;
761
+ let mut futex = futex_ref. 0 . borrow_mut ( ) ;
748
762
let data_race = & this. machine . data_race ;
749
763
750
764
// Each futex-wake happens-before the end of the futex wait
@@ -757,7 +771,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
757
771
return interp_ok ( false ) ;
758
772
} ;
759
773
let waiter = futex. waiters . remove ( i) . unwrap ( ) ;
760
- this. unblock_thread ( waiter. thread , BlockReason :: Futex { addr } ) ?;
774
+ drop ( futex) ;
775
+ this. unblock_thread ( waiter. thread , BlockReason :: Futex ) ?;
761
776
interp_ok ( true )
762
777
}
763
778
}
0 commit comments