1
1
use std:: cell:: RefCell ;
2
2
use std:: collections:: VecDeque ;
3
3
use std:: collections:: hash_map:: Entry ;
4
+ use std:: default:: Default ;
4
5
use std:: ops:: Not ;
5
6
use std:: rc:: Rc ;
6
7
use std:: time:: Duration ;
@@ -46,8 +47,6 @@ macro_rules! declare_id {
46
47
}
47
48
pub ( super ) use declare_id;
48
49
49
- declare_id ! ( MutexId ) ;
50
-
51
50
/// The mutex state.
52
51
#[ derive( Default , Debug ) ]
53
52
struct Mutex {
@@ -61,6 +60,21 @@ struct Mutex {
61
60
clock : VClock ,
62
61
}
63
62
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
+
64
78
declare_id ! ( RwLockId ) ;
65
79
66
80
/// The read-write lock state.
@@ -144,7 +158,6 @@ struct FutexWaiter {
144
158
/// The state of all synchronization objects.
145
159
#[ derive( Default , Debug ) ]
146
160
pub struct SynchronizationObjects {
147
- mutexes : IndexVec < MutexId , Mutex > ,
148
161
rwlocks : IndexVec < RwLockId , RwLock > ,
149
162
condvars : IndexVec < CondvarId , Condvar > ,
150
163
pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
@@ -155,17 +168,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
155
168
pub ( super ) trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
156
169
fn condvar_reacquire_mutex (
157
170
& mut self ,
158
- mutex : MutexId ,
171
+ mutex_ref : & MutexRef ,
159
172
retval : Scalar ,
160
173
dest : MPlaceTy < ' tcx > ,
161
174
) -> InterpResult < ' tcx > {
162
175
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) ) ) ;
166
179
} else {
167
180
// We can have it right now!
168
- this. mutex_lock ( mutex ) ;
181
+ this. mutex_lock ( mutex_ref ) ;
169
182
// Don't forget to write the return value.
170
183
this. write_scalar ( retval, & dest) ?;
171
184
}
@@ -174,10 +187,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
174
187
}
175
188
176
189
impl 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 ( )
179
192
}
180
-
181
193
pub fn rwlock_create ( & mut self ) -> RwLockId {
182
194
self . rwlocks . push ( Default :: default ( ) )
183
195
}
@@ -209,7 +221,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
209
221
pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
210
222
/// Helper for lazily initialized `alloc_extra.sync` data:
211
223
/// this forces an immediate init.
212
- fn lazy_sync_init < T : ' static + Copy > (
224
+ fn lazy_sync_init < T : ' static > (
213
225
& mut self ,
214
226
primitive : & MPlaceTy < ' tcx > ,
215
227
init_offset : Size ,
@@ -235,7 +247,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235
247
/// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
236
248
/// and stores that in `alloc_extra.sync`.
237
249
/// - 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 > (
239
254
& mut self ,
240
255
primitive : & MPlaceTy < ' tcx > ,
241
256
init_offset : Size ,
@@ -266,15 +281,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
266
281
let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
267
282
let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
268
283
if let Some ( data) = alloc_extra. get_sync :: < T > ( offset) {
269
- interp_ok ( * data)
284
+ interp_ok ( data. clone ( ) )
270
285
} else {
271
286
let data = missing_data ( ) ?;
272
- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
287
+ alloc_extra. sync . insert ( offset, Box :: new ( data. clone ( ) ) ) ;
273
288
interp_ok ( data)
274
289
}
275
290
} else {
276
291
let data = new_data ( this) ?;
277
- this. lazy_sync_init ( primitive, init_offset, data) ?;
292
+ this. lazy_sync_init ( primitive, init_offset, data. clone ( ) ) ?;
278
293
interp_ok ( data)
279
294
}
280
295
}
@@ -311,23 +326,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
311
326
312
327
#[ inline]
313
328
/// 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 ( )
317
331
}
318
332
319
333
#[ inline]
320
334
/// 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 ( )
324
337
}
325
338
326
339
/// 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 ) {
328
341
let this = self . eval_context_mut ( ) ;
329
342
let thread = this. active_thread ( ) ;
330
- let mutex = & mut this . machine . sync . mutexes [ id ] ;
343
+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
331
344
if let Some ( current_owner) = mutex. owner {
332
345
assert_eq ! ( thread, current_owner, "mutex already locked by another thread" ) ;
333
346
assert ! (
@@ -347,9 +360,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
347
360
/// count. If the lock count reaches 0, release the lock and potentially
348
361
/// give to a new owner. If the lock was not locked by the current thread,
349
362
/// 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 > > {
351
364
let this = self . eval_context_mut ( ) ;
352
- let mutex = & mut this . machine . sync . mutexes [ id ] ;
365
+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
353
366
interp_ok ( if let Some ( current_owner) = mutex. owner {
354
367
// Mutex is locked.
355
368
if current_owner != this. machine . threads . active_thread ( ) {
@@ -367,8 +380,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367
380
mutex. clock . clone_from ( clock)
368
381
} ) ;
369
382
}
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 ) ?;
372
389
}
373
390
}
374
391
Some ( old_lock_count)
@@ -385,24 +402,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385
402
#[ inline]
386
403
fn mutex_enqueue_and_block (
387
404
& mut self ,
388
- id : MutexId ,
405
+ mutex_ref : & MutexRef ,
389
406
retval_dest : Option < ( Scalar , MPlaceTy < ' tcx > ) > ,
390
407
) {
391
408
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" ) ;
393
410
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 ( ) ;
395
413
this. block_thread (
396
- BlockReason :: Mutex ( id ) ,
414
+ BlockReason :: Mutex ,
397
415
None ,
398
416
callback ! (
399
417
@capture<' tcx> {
400
- id : MutexId ,
418
+ mutex_ref : MutexRef ,
401
419
retval_dest: Option <( Scalar , MPlaceTy <' tcx>) >,
402
420
}
403
421
@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 ) ;
406
424
407
425
if let Some ( ( retval, dest) ) = retval_dest {
408
426
this. write_scalar( retval, & dest) ?;
@@ -623,14 +641,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
623
641
fn condvar_wait (
624
642
& mut self ,
625
643
condvar : CondvarId ,
626
- mutex : MutexId ,
644
+ mutex_ref : MutexRef ,
627
645
timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
628
646
retval_succ : Scalar ,
629
647
retval_timeout : Scalar ,
630
648
dest : MPlaceTy < ' tcx > ,
631
649
) -> InterpResult < ' tcx > {
632
650
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 ) ? {
634
652
if old_locked_count != 1 {
635
653
throw_unsup_format ! (
636
654
"awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -650,7 +668,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
650
668
callback ! (
651
669
@capture<' tcx> {
652
670
condvar: CondvarId ,
653
- mutex : MutexId ,
671
+ mutex_ref : MutexRef ,
654
672
retval_succ: Scalar ,
655
673
retval_timeout: Scalar ,
656
674
dest: MPlaceTy <' tcx>,
@@ -665,15 +683,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
665
683
}
666
684
// Try to acquire the mutex.
667
685
// 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)
669
687
}
670
688
@timeout = |this| {
671
689
// We have to remove the waiter from the queue again.
672
690
let thread = this. active_thread( ) ;
673
691
let waiters = & mut this. machine. sync. condvars[ condvar] . waiters;
674
692
waiters. retain( |waiter| * waiter != thread) ;
675
693
// Now get back the lock.
676
- this. condvar_reacquire_mutex( mutex , retval_timeout, dest)
694
+ this. condvar_reacquire_mutex( & mutex_ref , retval_timeout, dest)
677
695
}
678
696
) ,
679
697
) ;
0 commit comments