1
+ use std:: cell:: RefCell ;
1
2
use std:: collections:: VecDeque ;
2
3
use std:: collections:: hash_map:: Entry ;
4
+ use std:: default:: Default ;
3
5
use std:: ops:: Not ;
6
+ use std:: rc:: Rc ;
4
7
use std:: time:: Duration ;
5
8
6
9
use rustc_data_structures:: fx:: FxHashMap ;
@@ -44,8 +47,6 @@ macro_rules! declare_id {
44
47
}
45
48
pub ( super ) use declare_id;
46
49
47
- declare_id ! ( MutexId ) ;
48
-
49
50
/// The mutex state.
50
51
#[ derive( Default , Debug ) ]
51
52
struct Mutex {
@@ -59,6 +60,21 @@ struct Mutex {
59
60
clock : VClock ,
60
61
}
61
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
+
62
78
declare_id ! ( RwLockId ) ;
63
79
64
80
/// The read-write lock state.
@@ -133,7 +149,6 @@ struct FutexWaiter {
133
149
/// The state of all synchronization objects.
134
150
#[ derive( Default , Debug ) ]
135
151
pub struct SynchronizationObjects {
136
- mutexes : IndexVec < MutexId , Mutex > ,
137
152
rwlocks : IndexVec < RwLockId , RwLock > ,
138
153
condvars : IndexVec < CondvarId , Condvar > ,
139
154
pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
@@ -147,17 +162,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
147
162
pub ( super ) trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
148
163
fn condvar_reacquire_mutex (
149
164
& mut self ,
150
- mutex : MutexId ,
165
+ mutex_ref : & MutexRef ,
151
166
retval : Scalar ,
152
167
dest : MPlaceTy < ' tcx > ,
153
168
) -> InterpResult < ' tcx > {
154
169
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) ) ) ;
158
173
} else {
159
174
// We can have it right now!
160
- this. mutex_lock ( mutex ) ;
175
+ this. mutex_lock ( mutex_ref ) ;
161
176
// Don't forget to write the return value.
162
177
this. write_scalar ( retval, & dest) ?;
163
178
}
@@ -166,10 +181,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
166
181
}
167
182
168
183
impl 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 ( )
171
186
}
172
-
173
187
pub fn rwlock_create ( & mut self ) -> RwLockId {
174
188
self . rwlocks . push ( Default :: default ( ) )
175
189
}
@@ -201,7 +215,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
201
215
pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
202
216
/// Helper for lazily initialized `alloc_extra.sync` data:
203
217
/// this forces an immediate init.
204
- fn lazy_sync_init < T : ' static + Copy > (
218
+ fn lazy_sync_init < T : ' static > (
205
219
& mut self ,
206
220
primitive : & MPlaceTy < ' tcx > ,
207
221
init_offset : Size ,
@@ -227,7 +241,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227
241
/// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
228
242
/// and stores that in `alloc_extra.sync`.
229
243
/// - 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 > (
231
248
& mut self ,
232
249
primitive : & MPlaceTy < ' tcx > ,
233
250
init_offset : Size ,
@@ -258,15 +275,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
258
275
let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
259
276
let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
260
277
if let Some ( data) = alloc_extra. get_sync :: < T > ( offset) {
261
- interp_ok ( * data)
278
+ interp_ok ( data. clone ( ) )
262
279
} else {
263
280
let data = missing_data ( ) ?;
264
- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
281
+ alloc_extra. sync . insert ( offset, Box :: new ( data. clone ( ) ) ) ;
265
282
interp_ok ( data)
266
283
}
267
284
} else {
268
285
let data = new_data ( this) ?;
269
- this. lazy_sync_init ( primitive, init_offset, data) ?;
286
+ this. lazy_sync_init ( primitive, init_offset, data. clone ( ) ) ?;
270
287
interp_ok ( data)
271
288
}
272
289
}
@@ -298,23 +315,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
298
315
299
316
#[ inline]
300
317
/// 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 ( )
304
320
}
305
321
306
322
#[ inline]
307
323
/// 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 ( )
311
326
}
312
327
313
328
/// 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 ) {
315
330
let this = self . eval_context_mut ( ) ;
316
331
let thread = this. active_thread ( ) ;
317
- let mutex = & mut this . machine . sync . mutexes [ id ] ;
332
+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
318
333
if let Some ( current_owner) = mutex. owner {
319
334
assert_eq ! ( thread, current_owner, "mutex already locked by another thread" ) ;
320
335
assert ! (
@@ -334,9 +349,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
334
349
/// count. If the lock count reaches 0, release the lock and potentially
335
350
/// give to a new owner. If the lock was not locked by the current thread,
336
351
/// 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 > > {
338
353
let this = self . eval_context_mut ( ) ;
339
- let mutex = & mut this . machine . sync . mutexes [ id ] ;
354
+ let mut mutex = mutex_ref . 0 . borrow_mut ( ) ;
340
355
interp_ok ( if let Some ( current_owner) = mutex. owner {
341
356
// Mutex is locked.
342
357
if current_owner != this. machine . threads . active_thread ( ) {
@@ -354,8 +369,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
354
369
mutex. clock . clone_from ( clock)
355
370
} ) ;
356
371
}
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 ) ?;
359
378
}
360
379
}
361
380
Some ( old_lock_count)
@@ -372,24 +391,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
372
391
#[ inline]
373
392
fn mutex_enqueue_and_block (
374
393
& mut self ,
375
- id : MutexId ,
394
+ mutex_ref : & MutexRef ,
376
395
retval_dest : Option < ( Scalar , MPlaceTy < ' tcx > ) > ,
377
396
) {
378
397
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" ) ;
380
399
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 ( ) ;
382
402
this. block_thread (
383
- BlockReason :: Mutex ( id ) ,
403
+ BlockReason :: Mutex ,
384
404
None ,
385
405
callback ! (
386
406
@capture<' tcx> {
387
- id : MutexId ,
407
+ mutex_ref : MutexRef ,
388
408
retval_dest: Option <( Scalar , MPlaceTy <' tcx>) >,
389
409
}
390
410
@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 ) ;
393
413
394
414
if let Some ( ( retval, dest) ) = retval_dest {
395
415
this. write_scalar( retval, & dest) ?;
@@ -610,14 +630,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
610
630
fn condvar_wait (
611
631
& mut self ,
612
632
condvar : CondvarId ,
613
- mutex : MutexId ,
633
+ mutex_ref : MutexRef ,
614
634
timeout : Option < ( TimeoutClock , TimeoutAnchor , Duration ) > ,
615
635
retval_succ : Scalar ,
616
636
retval_timeout : Scalar ,
617
637
dest : MPlaceTy < ' tcx > ,
618
638
) -> InterpResult < ' tcx > {
619
639
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 ) ? {
621
641
if old_locked_count != 1 {
622
642
throw_unsup_format ! (
623
643
"awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -637,7 +657,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
637
657
callback ! (
638
658
@capture<' tcx> {
639
659
condvar: CondvarId ,
640
- mutex : MutexId ,
660
+ mutex_ref : MutexRef ,
641
661
retval_succ: Scalar ,
642
662
retval_timeout: Scalar ,
643
663
dest: MPlaceTy <' tcx>,
@@ -652,15 +672,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
652
672
}
653
673
// Try to acquire the mutex.
654
674
// 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)
656
676
}
657
677
@timeout = |this| {
658
678
// We have to remove the waiter from the queue again.
659
679
let thread = this. active_thread( ) ;
660
680
let waiters = & mut this. machine. sync. condvars[ condvar] . waiters;
661
681
waiters. retain( |waiter| * waiter != thread) ;
662
682
// Now get back the lock.
663
- this. condvar_reacquire_mutex( mutex , retval_timeout, dest)
683
+ this. condvar_reacquire_mutex( & mutex_ref , retval_timeout, dest)
664
684
}
665
685
) ,
666
686
) ;
0 commit comments