1+ use std:: cell:: RefCell ;
12use std:: collections:: VecDeque ;
23use std:: collections:: hash_map:: Entry ;
4+ use std:: default:: Default ;
35use std:: ops:: Not ;
6+ use std:: rc:: Rc ;
47use std:: time:: Duration ;
58
69use rustc_data_structures:: fx:: FxHashMap ;
@@ -44,8 +47,6 @@ macro_rules! declare_id {
4447}
4548pub ( super ) use declare_id;
4649
47- declare_id ! ( MutexId ) ;
48-
4950/// The mutex state.
5051#[ derive( Default , Debug ) ]
5152struct Mutex {
@@ -59,6 +60,21 @@ struct Mutex {
5960 clock : VClock ,
6061}
6162
63+ #[ derive( Default , Clone , Debug ) ]
64+ pub struct MutexRef ( Rc < RefCell < Mutex > > ) ;
65+
66+ impl MutexRef {
67+ fn new ( ) -> Self {
68+ MutexRef ( Rc :: new ( RefCell :: new ( Mutex :: default ( ) ) ) )
69+ }
70+ }
71+
72+ impl VisitProvenance for MutexRef {
73+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
74+ // Mutex contains no provenance.
75+ }
76+ }
77+
6278declare_id ! ( RwLockId ) ;
6379
6480/// The read-write lock state.
@@ -133,7 +149,6 @@ struct FutexWaiter {
133149/// The state of all synchronization objects.
134150#[ derive( Default , Debug ) ]
135151pub struct SynchronizationObjects {
136- mutexes : IndexVec < MutexId , Mutex > ,
137152 rwlocks : IndexVec < RwLockId , RwLock > ,
138153 condvars : IndexVec < CondvarId , Condvar > ,
139154 pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
@@ -147,17 +162,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
147162pub ( super ) trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
148163 fn condvar_reacquire_mutex (
149164 & mut self ,
150- mutex : MutexId ,
165+ mutex_ref : & MutexRef ,
151166 retval : Scalar ,
152167 dest : MPlaceTy < ' tcx > ,
153168 ) -> InterpResult < ' tcx > {
154169 let this = self . eval_context_mut ( ) ;
155- if this. mutex_is_locked ( mutex ) {
156- assert_ne ! ( this. mutex_get_owner( mutex ) , this. active_thread( ) ) ;
157- this. mutex_enqueue_and_block ( mutex , Some ( ( retval, dest) ) ) ;
170+ if this. mutex_is_locked ( mutex_ref ) {
171+ assert_ne ! ( this. mutex_get_owner( mutex_ref ) , this. active_thread( ) ) ;
172+ this. mutex_enqueue_and_block ( mutex_ref , Some ( ( retval, dest) ) ) ;
158173 } else {
159174 // We can have it right now!
160- this. mutex_lock ( mutex ) ;
175+ this. mutex_lock ( mutex_ref ) ;
161176 // Don't forget to write the return value.
162177 this. write_scalar ( retval, & dest) ?;
163178 }
@@ -166,10 +181,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
166181}
167182
168183impl SynchronizationObjects {
169- pub fn mutex_create ( & mut self ) -> MutexId {
170- self . mutexes . push ( Default :: default ( ) )
184+ pub fn mutex_create ( & mut self ) -> MutexRef {
185+ MutexRef :: new ( )
171186 }
172-
173187 pub fn rwlock_create ( & mut self ) -> RwLockId {
174188 self . rwlocks . push ( Default :: default ( ) )
175189 }
@@ -201,7 +215,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
201215pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
202216 /// Helper for lazily initialized `alloc_extra.sync` data:
203217 /// this forces an immediate init.
204- fn lazy_sync_init < T : ' static + Copy > (
218+ fn lazy_sync_init < T : ' static > (
205219 & mut self ,
206220 primitive : & MPlaceTy < ' tcx > ,
207221 init_offset : Size ,
@@ -227,7 +241,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227241 /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
228242 /// and stores that in `alloc_extra.sync`.
229243 /// - Otherwise, calls `new_data` to initialize the primitive.
230- fn lazy_sync_get_data < T : ' static + Copy > (
244+ ///
245+ /// The return value is a *clone* of the stored data, so if you intend to mutate it
246+ /// better wrap everything into an `Rc`.
247+ fn lazy_sync_get_data < T : ' static + Clone > (
231248 & mut self ,
232249 primitive : & MPlaceTy < ' tcx > ,
233250 init_offset : Size ,
@@ -258,15 +275,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
258275 let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
259276 let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
260277 if let Some ( data) = alloc_extra. get_sync :: < T > ( offset) {
261- interp_ok ( * data)
278+ interp_ok ( data. clone ( ) )
262279 } else {
263280 let data = missing_data ( ) ?;
264- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
281+ alloc_extra. sync . insert ( offset, Box :: new ( data. clone ( ) ) ) ;
265282 interp_ok ( data)
266283 }
267284 } else {
268285 let data = new_data ( this) ?;
269- this. lazy_sync_init ( primitive, init_offset, data) ?;
286+ this. lazy_sync_init ( primitive, init_offset, data. clone ( ) ) ?;
270287 interp_ok ( data)
271288 }
272289 }
@@ -298,23 +315,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
298315
299316 #[ inline]
300317 /// Get the id of the thread that currently owns this lock.
301- fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
302- let this = self . eval_context_ref ( ) ;
303- this. machine . sync . mutexes [ id] . owner . unwrap ( )
318+ fn mutex_get_owner ( & mut self , mutex_ref : & MutexRef ) -> ThreadId {
319+ mutex_ref. 0 . borrow ( ) . owner . unwrap ( )
304320 }
305321
306322 #[ inline]
307323 /// Check if locked.
308- fn mutex_is_locked ( & self , id : MutexId ) -> bool {
309- let this = self . eval_context_ref ( ) ;
310- this. machine . sync . mutexes [ id] . owner . is_some ( )
324+ fn mutex_is_locked ( & self , mutex_ref : & MutexRef ) -> bool {
325+ mutex_ref. 0 . borrow ( ) . owner . is_some ( )
311326 }
312327
313328 /// Lock by setting the mutex owner and increasing the lock count.
314- fn mutex_lock ( & mut self , id : MutexId ) {
329+ fn mutex_lock ( & mut self , mutex_ref : & MutexRef ) {
315330 let this = self . eval_context_mut ( ) ;
316331 let thread = this. active_thread ( ) ;
317- let mutex = & mut this . machine . sync . mutexes [ id ] ;
332+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
318333 if let Some ( current_owner) = mutex. owner {
319334 assert_eq ! ( thread, current_owner, "mutex already locked by another thread" ) ;
320335 assert ! (
@@ -334,9 +349,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
334349 /// count. If the lock count reaches 0, release the lock and potentially
335350 /// give to a new owner. If the lock was not locked by the current thread,
336351 /// return `None`.
337- fn mutex_unlock ( & mut self , id : MutexId ) -> InterpResult < ' tcx , Option < usize > > {
352+ fn mutex_unlock ( & mut self , mutex_ref : & MutexRef ) -> InterpResult < ' tcx , Option < usize > > {
338353 let this = self . eval_context_mut ( ) ;
339- let mutex = & mut this . machine . sync . mutexes [ id ] ;
354+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
340355 interp_ok ( if let Some ( current_owner) = mutex. owner {
341356 // Mutex is locked.
342357 if current_owner != this. machine . threads . active_thread ( ) {
@@ -354,8 +369,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
354369 mutex. clock . clone_from ( clock)
355370 } ) ;
356371 }
357- if let Some ( thread) = this. machine . sync . mutexes [ id] . queue . pop_front ( ) {
358- this. unblock_thread ( thread, BlockReason :: Mutex ( id) ) ?;
372+ let thread_id = mutex. queue . pop_front ( ) ;
373+ // We need to drop our mutex borrow before unblock_thread
374+ // because it will be borrowed again in the unblock callback.
375+ drop ( mutex) ;
376+ if thread_id. is_some ( ) {
377+ this. unblock_thread ( thread_id. unwrap ( ) , BlockReason :: Mutex ) ?;
359378 }
360379 }
361380 Some ( old_lock_count)
@@ -372,24 +391,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
372391 #[ inline]
373392 fn mutex_enqueue_and_block (
374393 & mut self ,
375- id : MutexId ,
394+ mutex_ref : & MutexRef ,
376395 retval_dest : Option < ( Scalar , MPlaceTy < ' tcx > ) > ,
377396 ) {
378397 let this = self . eval_context_mut ( ) ;
379- assert ! ( this. mutex_is_locked( id ) , "queing on unlocked mutex" ) ;
398+ assert ! ( this. mutex_is_locked( mutex_ref ) , "queuing on unlocked mutex" ) ;
380399 let thread = this. active_thread ( ) ;
381- this. machine . sync . mutexes [ id] . queue . push_back ( thread) ;
400+ mutex_ref. 0 . borrow_mut ( ) . queue . push_back ( thread) ;
401+ let mutex_ref = mutex_ref. clone ( ) ;
382402 this. block_thread (
383- BlockReason :: Mutex ( id ) ,
403+ BlockReason :: Mutex ,
384404 None ,
385405 callback ! (
386406 @capture<' tcx> {
387- id : MutexId ,
407+ mutex_ref : MutexRef ,
388408 retval_dest: Option <( Scalar , MPlaceTy <' tcx>) >,
389409 }
390410 @unblock = |this| {
391- assert!( !this. mutex_is_locked( id ) ) ;
392- this. mutex_lock( id ) ;
411+ assert!( !this. mutex_is_locked( & mutex_ref ) ) ;
412+ this. mutex_lock( & mutex_ref ) ;
393413
394414 if let Some ( ( retval, dest) ) = retval_dest {
395415 this. write_scalar( retval, & dest) ?;
@@ -610,14 +630,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
610630 fn condvar_wait (
611631 & mut self ,
612632 condvar : CondvarId ,
613- mutex : MutexId ,
633+ mutex_ref : MutexRef ,
614634 timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
615635 retval_succ : Scalar ,
616636 retval_timeout : Scalar ,
617637 dest : MPlaceTy < ' tcx > ,
618638 ) -> InterpResult < ' tcx > {
619639 let this = self . eval_context_mut ( ) ;
620- if let Some ( old_locked_count) = this. mutex_unlock ( mutex ) ? {
640+ if let Some ( old_locked_count) = this. mutex_unlock ( & mutex_ref ) ? {
621641 if old_locked_count != 1 {
622642 throw_unsup_format ! (
623643 "awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -637,7 +657,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
637657 callback ! (
638658 @capture<' tcx> {
639659 condvar: CondvarId ,
640- mutex : MutexId ,
660+ mutex_ref : MutexRef ,
641661 retval_succ: Scalar ,
642662 retval_timeout: Scalar ,
643663 dest: MPlaceTy <' tcx>,
@@ -652,15 +672,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
652672 }
653673 // Try to acquire the mutex.
654674 // The timeout only applies to the first wait (until the signal), not for mutex acquisition.
655- this. condvar_reacquire_mutex( mutex , retval_succ, dest)
675+ this. condvar_reacquire_mutex( & mutex_ref , retval_succ, dest)
656676 }
657677 @timeout = |this| {
658678 // We have to remove the waiter from the queue again.
659679 let thread = this. active_thread( ) ;
660680 let waiters = & mut this. machine. sync. condvars[ condvar] . waiters;
661681 waiters. retain( |waiter| * waiter != thread) ;
662682 // Now get back the lock.
663- this. condvar_reacquire_mutex( mutex , retval_timeout, dest)
683+ this. condvar_reacquire_mutex( & mutex_ref , retval_timeout, dest)
664684 }
665685 ) ,
666686 ) ;
0 commit comments