11use std:: cell:: RefCell ;
22use std:: collections:: VecDeque ;
33use std:: collections:: hash_map:: Entry ;
4+ use std:: default:: Default ;
45use std:: ops:: Not ;
56use std:: rc:: Rc ;
67use std:: time:: Duration ;
@@ -46,8 +47,6 @@ macro_rules! declare_id {
4647}
4748pub ( super ) use declare_id;
4849
49- declare_id ! ( MutexId ) ;
50-
5150/// The mutex state.
5251#[ derive( Default , Debug ) ]
5352struct Mutex {
@@ -61,6 +60,21 @@ struct Mutex {
6160 clock : VClock ,
6261}
6362
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+
6478declare_id ! ( RwLockId ) ;
6579
6680/// The read-write lock state.
@@ -144,7 +158,6 @@ struct FutexWaiter {
144158/// The state of all synchronization objects.
145159#[ derive( Default , Debug ) ]
146160pub struct SynchronizationObjects {
147- mutexes : IndexVec < MutexId , Mutex > ,
148161 rwlocks : IndexVec < RwLockId , RwLock > ,
149162 condvars : IndexVec < CondvarId , Condvar > ,
150163 pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
@@ -155,17 +168,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
155168pub ( super ) trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
156169 fn condvar_reacquire_mutex (
157170 & mut self ,
158- mutex : MutexId ,
171+ mutex_ref : & MutexRef ,
159172 retval : Scalar ,
160173 dest : MPlaceTy < ' tcx > ,
161174 ) -> InterpResult < ' tcx > {
162175 let this = self . eval_context_mut ( ) ;
163- if this. mutex_is_locked ( mutex ) {
164- assert_ne ! ( this. mutex_get_owner( mutex ) , this. active_thread( ) ) ;
165- this. mutex_enqueue_and_block ( mutex , Some ( ( retval, dest) ) ) ;
176+ if this. mutex_is_locked ( mutex_ref ) {
177+ assert_ne ! ( this. mutex_get_owner( mutex_ref ) , this. active_thread( ) ) ;
178+ this. mutex_enqueue_and_block ( mutex_ref , Some ( ( retval, dest) ) ) ;
166179 } else {
167180 // We can have it right now!
168- this. mutex_lock ( mutex ) ;
181+ this. mutex_lock ( mutex_ref ) ;
169182 // Don't forget to write the return value.
170183 this. write_scalar ( retval, & dest) ?;
171184 }
@@ -174,10 +187,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
174187}
175188
176189impl SynchronizationObjects {
177- pub fn mutex_create ( & mut self ) -> MutexId {
178- self . mutexes . push ( Default :: default ( ) )
190+ pub fn mutex_create ( & mut self ) -> MutexRef {
191+ MutexRef :: new ( )
179192 }
180-
181193 pub fn rwlock_create ( & mut self ) -> RwLockId {
182194 self . rwlocks . push ( Default :: default ( ) )
183195 }
@@ -209,7 +221,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
209221pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
210222 /// Helper for lazily initialized `alloc_extra.sync` data:
211223 /// this forces an immediate init.
212- fn lazy_sync_init < T : ' static + Copy > (
224+ fn lazy_sync_init < T : ' static > (
213225 & mut self ,
214226 primitive : & MPlaceTy < ' tcx > ,
215227 init_offset : Size ,
@@ -235,7 +247,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235247 /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
236248 /// and stores that in `alloc_extra.sync`.
237249 /// - Otherwise, calls `new_data` to initialize the primitive.
238- fn lazy_sync_get_data < T : ' static + Copy > (
250+ ///
251+ /// The return value is a *clone* of the stored data, so if you intend to mutate it
252+ /// better wrap everything into an `Rc`.
253+ fn lazy_sync_get_data < T : ' static + Clone > (
239254 & mut self ,
240255 primitive : & MPlaceTy < ' tcx > ,
241256 init_offset : Size ,
@@ -266,15 +281,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
266281 let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
267282 let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
268283 if let Some ( data) = alloc_extra. get_sync :: < T > ( offset) {
269- interp_ok ( * data)
284+ interp_ok ( data. clone ( ) )
270285 } else {
271286 let data = missing_data ( ) ?;
272- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
287+ alloc_extra. sync . insert ( offset, Box :: new ( data. clone ( ) ) ) ;
273288 interp_ok ( data)
274289 }
275290 } else {
276291 let data = new_data ( this) ?;
277- this. lazy_sync_init ( primitive, init_offset, data) ?;
292+ this. lazy_sync_init ( primitive, init_offset, data. clone ( ) ) ?;
278293 interp_ok ( data)
279294 }
280295 }
@@ -311,23 +326,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
311326
312327 #[ inline]
313328 /// Get the id of the thread that currently owns this lock.
314- fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
315- let this = self . eval_context_ref ( ) ;
316- this. machine . sync . mutexes [ id] . owner . unwrap ( )
329+ fn mutex_get_owner ( & mut self , mutex_ref : & MutexRef ) -> ThreadId {
330+ mutex_ref. 0 . borrow ( ) . owner . unwrap ( )
317331 }
318332
319333 #[ inline]
320334 /// Check if locked.
321- fn mutex_is_locked ( & self , id : MutexId ) -> bool {
322- let this = self . eval_context_ref ( ) ;
323- this. machine . sync . mutexes [ id] . owner . is_some ( )
335+ fn mutex_is_locked ( & self , mutex_ref : & MutexRef ) -> bool {
336+ mutex_ref. 0 . borrow ( ) . owner . is_some ( )
324337 }
325338
326339 /// Lock by setting the mutex owner and increasing the lock count.
327- fn mutex_lock ( & mut self , id : MutexId ) {
340+ fn mutex_lock ( & mut self , mutex_ref : & MutexRef ) {
328341 let this = self . eval_context_mut ( ) ;
329342 let thread = this. active_thread ( ) ;
330- let mutex = & mut this . machine . sync . mutexes [ id ] ;
343+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
331344 if let Some ( current_owner) = mutex. owner {
332345 assert_eq ! ( thread, current_owner, "mutex already locked by another thread" ) ;
333346 assert ! (
@@ -347,9 +360,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
347360 /// count. If the lock count reaches 0, release the lock and potentially
348361 /// give to a new owner. If the lock was not locked by the current thread,
349362 /// return `None`.
350- fn mutex_unlock ( & mut self , id : MutexId ) -> InterpResult < ' tcx , Option < usize > > {
363+ fn mutex_unlock ( & mut self , mutex_ref : & MutexRef ) -> InterpResult < ' tcx , Option < usize > > {
351364 let this = self . eval_context_mut ( ) ;
352- let mutex = & mut this . machine . sync . mutexes [ id ] ;
365+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
353366 interp_ok ( if let Some ( current_owner) = mutex. owner {
354367 // Mutex is locked.
355368 if current_owner != this. machine . threads . active_thread ( ) {
@@ -367,8 +380,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367380 mutex. clock . clone_from ( clock)
368381 } ) ;
369382 }
370- if let Some ( thread) = this. machine . sync . mutexes [ id] . queue . pop_front ( ) {
371- this. unblock_thread ( thread, BlockReason :: Mutex ( id) ) ?;
383+ let thread_id = mutex. queue . pop_front ( ) ;
384+ // We need to drop our mutex borrow before unblock_thread
385+ // because it will be borrowed again in the unblock callback.
386+ drop ( mutex) ;
387+ if thread_id. is_some ( ) {
388+ this. unblock_thread ( thread_id. unwrap ( ) , BlockReason :: Mutex ) ?;
372389 }
373390 }
374391 Some ( old_lock_count)
@@ -385,24 +402,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385402 #[ inline]
386403 fn mutex_enqueue_and_block (
387404 & mut self ,
388- id : MutexId ,
405+ mutex_ref : & MutexRef ,
389406 retval_dest : Option < ( Scalar , MPlaceTy < ' tcx > ) > ,
390407 ) {
391408 let this = self . eval_context_mut ( ) ;
392- assert ! ( this. mutex_is_locked( id ) , "queing on unlocked mutex" ) ;
409+ assert ! ( this. mutex_is_locked( mutex_ref ) , "queuing on unlocked mutex" ) ;
393410 let thread = this. active_thread ( ) ;
394- this. machine . sync . mutexes [ id] . queue . push_back ( thread) ;
411+ mutex_ref. 0 . borrow_mut ( ) . queue . push_back ( thread) ;
412+ let mutex_ref = mutex_ref. clone ( ) ;
395413 this. block_thread (
396- BlockReason :: Mutex ( id ) ,
414+ BlockReason :: Mutex ,
397415 None ,
398416 callback ! (
399417 @capture<' tcx> {
400- id : MutexId ,
418+ mutex_ref : MutexRef ,
401419 retval_dest: Option <( Scalar , MPlaceTy <' tcx>) >,
402420 }
403421 @unblock = |this| {
404- assert!( !this. mutex_is_locked( id ) ) ;
405- this. mutex_lock( id ) ;
422+ assert!( !this. mutex_is_locked( & mutex_ref ) ) ;
423+ this. mutex_lock( & mutex_ref ) ;
406424
407425 if let Some ( ( retval, dest) ) = retval_dest {
408426 this. write_scalar( retval, & dest) ?;
@@ -623,14 +641,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
623641 fn condvar_wait (
624642 & mut self ,
625643 condvar : CondvarId ,
626- mutex : MutexId ,
644+ mutex_ref : MutexRef ,
627645 timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
628646 retval_succ : Scalar ,
629647 retval_timeout : Scalar ,
630648 dest : MPlaceTy < ' tcx > ,
631649 ) -> InterpResult < ' tcx > {
632650 let this = self . eval_context_mut ( ) ;
633- if let Some ( old_locked_count) = this. mutex_unlock ( mutex ) ? {
651+ if let Some ( old_locked_count) = this. mutex_unlock ( & mutex_ref ) ? {
634652 if old_locked_count != 1 {
635653 throw_unsup_format ! (
636654 "awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -650,7 +668,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
650668 callback ! (
651669 @capture<' tcx> {
652670 condvar: CondvarId ,
653- mutex : MutexId ,
671+ mutex_ref : MutexRef ,
654672 retval_succ: Scalar ,
655673 retval_timeout: Scalar ,
656674 dest: MPlaceTy <' tcx>,
@@ -665,15 +683,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
665683 }
666684 // Try to acquire the mutex.
667685 // The timeout only applies to the first wait (until the signal), not for mutex acquisition.
668- this. condvar_reacquire_mutex( mutex , retval_succ, dest)
686+ this. condvar_reacquire_mutex( & mutex_ref , retval_succ, dest)
669687 }
670688 @timeout = |this| {
671689 // We have to remove the waiter from the queue again.
672690 let thread = this. active_thread( ) ;
673691 let waiters = & mut this. machine. sync. condvars[ condvar] . waiters;
674692 waiters. retain( |waiter| * waiter != thread) ;
675693 // Now get back the lock.
676- this. condvar_reacquire_mutex( mutex , retval_timeout, dest)
694+ this. condvar_reacquire_mutex( & mutex_ref , retval_timeout, dest)
677695 }
678696 ) ,
679697 ) ;
0 commit comments