1+ use std:: cell:: RefCell ;
12use std:: collections:: VecDeque ;
23use std:: collections:: hash_map:: Entry ;
34use std:: ops:: Not ;
5+ use std:: rc:: Rc ;
46use std:: time:: Duration ;
57
68use rustc_abi:: Size ;
@@ -121,6 +123,15 @@ struct Futex {
121123 clock : VClock ,
122124}
123125
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+
124135/// A thread waiting on a futex.
125136#[ derive( Debug ) ]
126137struct FutexWaiter {
@@ -137,9 +148,6 @@ pub struct SynchronizationObjects {
137148 rwlocks : IndexVec < RwLockId , RwLock > ,
138149 condvars : IndexVec < CondvarId , Condvar > ,
139150 pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
140-
141- /// Futex info for the futex at the given address.
142- futexes : FxHashMap < u64 , Futex > ,
143151}
144152
145153// Private extension trait for local helper methods
@@ -184,7 +192,7 @@ impl SynchronizationObjects {
184192}
185193
186194impl < ' 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 > {
188196 self . sync . get ( & offset) . and_then ( |s| s. downcast_ref :: < T > ( ) )
189197 }
190198}
@@ -273,27 +281,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
273281
274282 /// Get the synchronization primitive associated with the given pointer,
275283 /// or initialize a new one.
284+ ///
285+ /// Return `None` if this pointer does not point to at least 1 byte of mutable memory.
276286 fn get_sync_or_init < ' a , T : ' static > (
277287 & ' a mut self ,
278288 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 >
281291 where
282292 ' tcx : ' a ,
283293 {
284294 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 ( ) ;
291304 // Due to borrow checker reasons, we have to do the lookup twice.
292305 if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
293- let new = new ( machine) ? ;
306+ let new = new ( machine) ;
294307 alloc_extra. sync . insert ( offset, Box :: new ( new) ) ;
295308 }
296- interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
309+ Some ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
297310 }
298311
299312 #[ inline]
@@ -690,7 +703,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
690703 /// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
691704 fn futex_wait (
692705 & mut self ,
693- addr : u64 ,
706+ futex_ref : FutexRef ,
694707 bitset : u32 ,
695708 timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
696709 retval_succ : Scalar ,
@@ -700,23 +713,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
700713 ) {
701714 let this = self . eval_context_mut ( ) ;
702715 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 ( ) ;
704717 let waiters = & mut futex. waiters ;
705718 assert ! ( waiters. iter( ) . all( |waiter| waiter. thread != thread) , "thread is already waiting" ) ;
706719 waiters. push_back ( FutexWaiter { thread, bitset } ) ;
720+ drop ( futex) ;
721+
707722 this. block_thread (
708- BlockReason :: Futex { addr } ,
723+ BlockReason :: Futex ,
709724 timeout,
710725 callback ! (
711726 @capture<' tcx> {
712- addr : u64 ,
727+ futex_ref : FutexRef ,
713728 retval_succ: Scalar ,
714729 retval_timeout: Scalar ,
715730 dest: MPlaceTy <' tcx>,
716731 errno_timeout: IoError ,
717732 }
718733 @unblock = |this| {
719- let futex = this . machine . sync . futexes . get ( & addr ) . unwrap ( ) ;
734+ let futex = futex_ref . 0 . borrow ( ) ;
720735 // Acquire the clock of the futex.
721736 if let Some ( data_race) = & this. machine. data_race {
722737 data_race. acquire_clock( & futex. clock, & this. machine. threads) ;
@@ -728,7 +743,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728743 @timeout = |this| {
729744 // Remove the waiter from the futex.
730745 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 ( ) ;
732747 futex. waiters. retain( |waiter| waiter. thread != thread) ;
733748 // Set errno and write return value.
734749 this. set_last_error( errno_timeout) ?;
@@ -739,12 +754,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
739754 ) ;
740755 }
741756
757+ /// Wake up the first thread in the queue that matches any of the bits in the bitset.
742758 /// 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 > {
744760 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 ( ) ;
748762 let data_race = & this. machine . data_race ;
749763
750764 // Each futex-wake happens-before the end of the futex wait
@@ -757,7 +771,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
757771 return interp_ok ( false ) ;
758772 } ;
759773 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 ) ?;
761776 interp_ok ( true )
762777 }
763778}
0 commit comments