@@ -67,6 +67,11 @@ impl MutexRef {
67
67
fn new ( ) -> Self {
68
68
MutexRef ( Rc :: new ( RefCell :: new ( Mutex :: default ( ) ) ) )
69
69
}
70
+
71
+ /// Get the id of the thread that currently owns this lock, or `None` if it is not locked.
72
+ pub fn owner ( & self ) -> Option < ThreadId > {
73
+ self . 0 . borrow ( ) . owner
74
+ }
70
75
}
71
76
72
77
impl VisitProvenance for MutexRef {
@@ -75,8 +80,6 @@ impl VisitProvenance for MutexRef {
75
80
}
76
81
}
77
82
78
- declare_id ! ( RwLockId ) ;
79
-
80
83
/// The read-write lock state.
81
84
#[ derive( Default , Debug ) ]
82
85
struct RwLock {
@@ -111,6 +114,49 @@ struct RwLock {
111
114
clock_current_readers : VClock ,
112
115
}
113
116
117
+ impl RwLock {
118
+ #[ inline]
119
+ /// Check if locked.
120
+ fn is_locked ( & self ) -> bool {
121
+ trace ! (
122
+ "rwlock_is_locked: writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)" ,
123
+ self . writer,
124
+ self . readers. len( ) ,
125
+ ) ;
126
+ self . writer . is_some ( ) || self . readers . is_empty ( ) . not ( )
127
+ }
128
+
129
+ /// Check if write locked.
130
+ #[ inline]
131
+ fn is_write_locked ( & self ) -> bool {
132
+ trace ! ( "rwlock_is_write_locked: writer is {:?}" , self . writer) ;
133
+ self . writer . is_some ( )
134
+ }
135
+ }
136
+
137
+ #[ derive( Default , Clone , Debug ) ]
138
+ pub struct RwLockRef ( Rc < RefCell < RwLock > > ) ;
139
+
140
+ impl RwLockRef {
141
+ fn new ( ) -> Self {
142
+ RwLockRef ( Rc :: new ( RefCell :: new ( RwLock :: default ( ) ) ) )
143
+ }
144
+
145
+ pub fn is_locked ( & self ) -> bool {
146
+ self . 0 . borrow ( ) . is_locked ( )
147
+ }
148
+
149
+ pub fn is_write_locked ( & self ) -> bool {
150
+ self . 0 . borrow ( ) . is_write_locked ( )
151
+ }
152
+ }
153
+
154
+ impl VisitProvenance for RwLockRef {
155
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
156
+ // RwLockRef contains no provenance.
157
+ }
158
+ }
159
+
114
160
declare_id ! ( CondvarId ) ;
115
161
116
162
/// The conditional variable state.
@@ -164,7 +210,6 @@ struct FutexWaiter {
164
210
/// The state of all synchronization objects.
165
211
#[ derive( Default , Debug ) ]
166
212
pub struct SynchronizationObjects {
167
- rwlocks : IndexVec < RwLockId , RwLock > ,
168
213
condvars : IndexVec < CondvarId , Condvar > ,
169
214
pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
170
215
}
@@ -174,17 +219,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
174
219
pub ( super ) trait EvalContextExtPriv < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
175
220
fn condvar_reacquire_mutex (
176
221
& mut self ,
177
- mutex_ref : & MutexRef ,
222
+ mutex_ref : MutexRef ,
178
223
retval : Scalar ,
179
224
dest : MPlaceTy < ' tcx > ,
180
225
) -> InterpResult < ' tcx > {
181
226
let this = self . eval_context_mut ( ) ;
182
- if this . mutex_is_locked ( mutex_ref) {
183
- assert_ne ! ( this . mutex_get_owner ( mutex_ref ) , this. active_thread( ) ) ;
227
+ if let Some ( owner ) = mutex_ref. owner ( ) {
228
+ assert_ne ! ( owner , this. active_thread( ) ) ;
184
229
this. mutex_enqueue_and_block ( mutex_ref, Some ( ( retval, dest) ) ) ;
185
230
} else {
186
231
// We can have it right now!
187
- this. mutex_lock ( mutex_ref) ;
232
+ this. mutex_lock ( & mutex_ref) ;
188
233
// Don't forget to write the return value.
189
234
this. write_scalar ( retval, & dest) ?;
190
235
}
@@ -196,8 +241,8 @@ impl SynchronizationObjects {
196
241
pub fn mutex_create ( & mut self ) -> MutexRef {
197
242
MutexRef :: new ( )
198
243
}
199
- pub fn rwlock_create ( & mut self ) -> RwLockId {
200
- self . rwlocks . push ( Default :: default ( ) )
244
+ pub fn rwlock_create ( & mut self ) -> RwLockRef {
245
+ RwLockRef :: new ( )
201
246
}
202
247
203
248
pub fn condvar_create ( & mut self ) -> CondvarId {
@@ -334,18 +379,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
334
379
Some ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
335
380
}
336
381
337
- #[ inline]
338
- /// Get the id of the thread that currently owns this lock.
339
- fn mutex_get_owner ( & self , mutex_ref : & MutexRef ) -> ThreadId {
340
- mutex_ref. 0 . borrow ( ) . owner . unwrap ( )
341
- }
342
-
343
- #[ inline]
344
- /// Check if locked.
345
- fn mutex_is_locked ( & self , mutex_ref : & MutexRef ) -> bool {
346
- mutex_ref. 0 . borrow ( ) . owner . is_some ( )
347
- }
348
-
349
382
/// Lock by setting the mutex owner and increasing the lock count.
350
383
fn mutex_lock ( & mut self , mutex_ref : & MutexRef ) {
351
384
let this = self . eval_context_mut ( ) ;
@@ -413,14 +446,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
413
446
#[ inline]
414
447
fn mutex_enqueue_and_block (
415
448
& mut self ,
416
- mutex_ref : & MutexRef ,
449
+ mutex_ref : MutexRef ,
417
450
retval_dest : Option < ( Scalar , MPlaceTy < ' tcx > ) > ,
418
451
) {
419
452
let this = self . eval_context_mut ( ) ;
420
- assert ! ( this. mutex_is_locked( mutex_ref) , "queuing on unlocked mutex" ) ;
421
453
let thread = this. active_thread ( ) ;
422
- mutex_ref. 0 . borrow_mut ( ) . queue . push_back ( thread) ;
423
- let mutex_ref = mutex_ref. clone ( ) ;
454
+ let mut mutex = mutex_ref. 0 . borrow_mut ( ) ;
455
+ mutex. queue . push_back ( thread) ;
456
+ assert ! ( mutex. owner. is_some( ) , "queuing on unlocked mutex" ) ;
457
+ drop ( mutex) ;
424
458
this. block_thread (
425
459
BlockReason :: Mutex ,
426
460
None ,
@@ -432,7 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
432
466
|this, unblock: UnblockKind | {
433
467
assert_eq!( unblock, UnblockKind :: Ready ) ;
434
468
435
- assert!( !this . mutex_is_locked ( & mutex_ref ) ) ;
469
+ assert!( mutex_ref . owner ( ) . is_none ( ) ) ;
436
470
this. mutex_lock( & mutex_ref) ;
437
471
438
472
if let Some ( ( retval, dest) ) = retval_dest {
@@ -445,37 +479,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
445
479
) ;
446
480
}
447
481
448
- #[ inline]
449
- /// Check if locked.
450
- fn rwlock_is_locked ( & self , id : RwLockId ) -> bool {
451
- let this = self . eval_context_ref ( ) ;
452
- let rwlock = & this. machine . sync . rwlocks [ id] ;
453
- trace ! (
454
- "rwlock_is_locked: {:?} writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)" ,
455
- id,
456
- rwlock. writer,
457
- rwlock. readers. len( ) ,
458
- ) ;
459
- rwlock. writer . is_some ( ) || rwlock. readers . is_empty ( ) . not ( )
460
- }
461
-
462
- /// Check if write locked.
463
- #[ inline]
464
- fn rwlock_is_write_locked ( & self , id : RwLockId ) -> bool {
465
- let this = self . eval_context_ref ( ) ;
466
- let rwlock = & this. machine . sync . rwlocks [ id] ;
467
- trace ! ( "rwlock_is_write_locked: {:?} writer is {:?}" , id, rwlock. writer) ;
468
- rwlock. writer . is_some ( )
469
- }
470
-
471
482
/// Read-lock the lock by adding the `reader` the list of threads that own
472
483
/// this lock.
473
- fn rwlock_reader_lock ( & mut self , id : RwLockId ) {
484
+ fn rwlock_reader_lock ( & mut self , rwlock_ref : & RwLockRef ) {
474
485
let this = self . eval_context_mut ( ) ;
475
486
let thread = this. active_thread ( ) ;
476
- assert ! ( !this . rwlock_is_write_locked ( id ) , "the lock is write locked" ) ;
477
- trace ! ( "rwlock_reader_lock: {:?} now also held (one more time) by {:?}" , id , thread ) ;
478
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
487
+ trace ! ( "rwlock_reader_lock: now also held (one more time) by {:?}" , thread ) ;
488
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
489
+ assert ! ( ! rwlock. is_write_locked ( ) , "the lock is write locked" ) ;
479
490
let count = rwlock. readers . entry ( thread) . or_insert ( 0 ) ;
480
491
* count = count. strict_add ( 1 ) ;
481
492
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
@@ -485,20 +496,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485
496
486
497
/// Try read-unlock the lock for the current threads and potentially give the lock to a new owner.
487
498
/// Returns `true` if succeeded, `false` if this `reader` did not hold the lock.
488
- fn rwlock_reader_unlock ( & mut self , id : RwLockId ) -> InterpResult < ' tcx , bool > {
499
+ fn rwlock_reader_unlock ( & mut self , rwlock_ref : & RwLockRef ) -> InterpResult < ' tcx , bool > {
489
500
let this = self . eval_context_mut ( ) ;
490
501
let thread = this. active_thread ( ) ;
491
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
502
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
492
503
match rwlock. readers . entry ( thread) {
493
504
Entry :: Occupied ( mut entry) => {
494
505
let count = entry. get_mut ( ) ;
495
506
assert ! ( * count > 0 , "rwlock locked with count == 0" ) ;
496
507
* count -= 1 ;
497
508
if * count == 0 {
498
- trace ! ( "rwlock_reader_unlock: {:?} no longer held by {:?}" , id , thread) ;
509
+ trace ! ( "rwlock_reader_unlock: no longer held by {:?}" , thread) ;
499
510
entry. remove ( ) ;
500
511
} else {
501
- trace ! ( "rwlock_reader_unlock: {:?} held one less time by {:?}" , id , thread) ;
512
+ trace ! ( "rwlock_reader_unlock: held one less time by {:?}" , thread) ;
502
513
}
503
514
}
504
515
Entry :: Vacant ( _) => return interp_ok ( false ) , // we did not even own this lock
@@ -511,15 +522,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
511
522
}
512
523
513
524
// The thread was a reader. If the lock is not held any more, give it to a writer.
514
- if this . rwlock_is_locked ( id ) . not ( ) {
525
+ if rwlock . is_locked ( ) . not ( ) {
515
526
// All the readers are finished, so set the writer data-race handle to the value
516
527
// of the union of all reader data race handles, since the set of readers
517
528
// happen-before the writers
518
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
519
- rwlock . clock_unlocked . clone_from ( & rwlock . clock_current_readers ) ;
529
+ let rwlock_ref = & mut * rwlock ;
530
+ rwlock_ref . clock_unlocked . clone_from ( & rwlock_ref . clock_current_readers ) ;
520
531
// See if there is a thread to unblock.
521
- if let Some ( writer) = rwlock. writer_queue . pop_front ( ) {
522
- this. unblock_thread ( writer, BlockReason :: RwLock ( id) ) ?;
532
+ if let Some ( writer) = rwlock_ref. writer_queue . pop_front ( ) {
533
+ drop ( rwlock) ; // make RefCell available for unblock callback
534
+ this. unblock_thread ( writer, BlockReason :: RwLock ) ?;
523
535
}
524
536
}
525
537
interp_ok ( true )
@@ -530,26 +542,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
530
542
#[ inline]
531
543
fn rwlock_enqueue_and_block_reader (
532
544
& mut self ,
533
- id : RwLockId ,
545
+ rwlock_ref : RwLockRef ,
534
546
retval : Scalar ,
535
547
dest : MPlaceTy < ' tcx > ,
536
548
) {
537
549
let this = self . eval_context_mut ( ) ;
538
550
let thread = this. active_thread ( ) ;
539
- assert ! ( this. rwlock_is_write_locked( id) , "read-queueing on not write locked rwlock" ) ;
540
- this. machine . sync . rwlocks [ id] . reader_queue . push_back ( thread) ;
551
+ let mut rwlock = rwlock_ref. 0 . borrow_mut ( ) ;
552
+ rwlock. reader_queue . push_back ( thread) ;
553
+ assert ! ( rwlock. is_write_locked( ) , "read-queueing on not write locked rwlock" ) ;
554
+ drop ( rwlock) ;
541
555
this. block_thread (
542
- BlockReason :: RwLock ( id ) ,
556
+ BlockReason :: RwLock ,
543
557
None ,
544
558
callback ! (
545
559
@capture<' tcx> {
546
- id : RwLockId ,
560
+ rwlock_ref : RwLockRef ,
547
561
retval: Scalar ,
548
562
dest: MPlaceTy <' tcx>,
549
563
}
550
564
|this, unblock: UnblockKind | {
551
565
assert_eq!( unblock, UnblockKind :: Ready ) ;
552
- this. rwlock_reader_lock( id ) ;
566
+ this. rwlock_reader_lock( & rwlock_ref ) ;
553
567
this. write_scalar( retval, & dest) ?;
554
568
interp_ok( ( ) )
555
569
}
@@ -559,12 +573,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
559
573
560
574
/// Lock by setting the writer that owns the lock.
561
575
#[ inline]
562
- fn rwlock_writer_lock ( & mut self , id : RwLockId ) {
576
+ fn rwlock_writer_lock ( & mut self , rwlock_ref : & RwLockRef ) {
563
577
let this = self . eval_context_mut ( ) ;
564
578
let thread = this. active_thread ( ) ;
565
- assert ! ( !this. rwlock_is_locked( id) , "the rwlock is already locked" ) ;
566
- trace ! ( "rwlock_writer_lock: {:?} now held by {:?}" , id, thread) ;
567
- let rwlock = & mut this. machine . sync . rwlocks [ id] ;
579
+ trace ! ( "rwlock_writer_lock: now held by {:?}" , thread) ;
580
+
581
+ let mut rwlock = rwlock_ref. 0 . borrow_mut ( ) ;
582
+ assert ! ( !rwlock. is_locked( ) , "the rwlock is already locked" ) ;
568
583
rwlock. writer = Some ( thread) ;
569
584
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
570
585
data_race. acquire_clock ( & rwlock. clock_unlocked , & this. machine . threads ) ;
@@ -574,35 +589,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
574
589
/// Try to unlock an rwlock held by the current thread.
575
590
/// Return `false` if it is held by another thread.
576
591
#[ inline]
577
- fn rwlock_writer_unlock ( & mut self , id : RwLockId ) -> InterpResult < ' tcx , bool > {
592
+ fn rwlock_writer_unlock ( & mut self , rwlock_ref : & RwLockRef ) -> InterpResult < ' tcx , bool > {
578
593
let this = self . eval_context_mut ( ) ;
579
594
let thread = this. active_thread ( ) ;
580
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
595
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
581
596
interp_ok ( if let Some ( current_writer) = rwlock. writer {
582
597
if current_writer != thread {
583
598
// Only the owner can unlock the rwlock.
584
599
return interp_ok ( false ) ;
585
600
}
586
601
rwlock. writer = None ;
587
- trace ! ( "rwlock_writer_unlock: {:?} unlocked by {:?}" , id , thread) ;
602
+ trace ! ( "rwlock_writer_unlock: unlocked by {:?}" , thread) ;
588
603
// Record release clock for next lock holder.
589
604
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
590
605
data_race. release_clock ( & this. machine . threads , |clock| {
591
606
rwlock. clock_unlocked . clone_from ( clock)
592
607
} ) ;
593
608
}
609
+
594
610
// The thread was a writer.
595
611
//
596
612
// We are prioritizing writers here against the readers. As a
597
613
// result, not only readers can starve writers, but also writers can
598
614
// starve readers.
599
615
if let Some ( writer) = rwlock. writer_queue . pop_front ( ) {
600
- this. unblock_thread ( writer, BlockReason :: RwLock ( id) ) ?;
616
+ drop ( rwlock) ; // make RefCell available for unblock callback
617
+ this. unblock_thread ( writer, BlockReason :: RwLock ) ?;
601
618
} else {
602
619
// Take the entire read queue and wake them all up.
603
620
let readers = std:: mem:: take ( & mut rwlock. reader_queue ) ;
621
+ drop ( rwlock) ; // make RefCell available for unblock callback
604
622
for reader in readers {
605
- this. unblock_thread ( reader, BlockReason :: RwLock ( id ) ) ?;
623
+ this. unblock_thread ( reader, BlockReason :: RwLock ) ?;
606
624
}
607
625
}
608
626
true
@@ -616,26 +634,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
616
634
#[ inline]
617
635
fn rwlock_enqueue_and_block_writer (
618
636
& mut self ,
619
- id : RwLockId ,
637
+ rwlock_ref : RwLockRef ,
620
638
retval : Scalar ,
621
639
dest : MPlaceTy < ' tcx > ,
622
640
) {
623
641
let this = self . eval_context_mut ( ) ;
624
- assert ! ( this. rwlock_is_locked( id) , "write-queueing on unlocked rwlock" ) ;
625
642
let thread = this. active_thread ( ) ;
626
- this. machine . sync . rwlocks [ id] . writer_queue . push_back ( thread) ;
643
+ let mut rwlock = rwlock_ref. 0 . borrow_mut ( ) ;
644
+ rwlock. writer_queue . push_back ( thread) ;
645
+ assert ! ( rwlock. is_locked( ) , "write-queueing on unlocked rwlock" ) ;
646
+ drop ( rwlock) ;
627
647
this. block_thread (
628
- BlockReason :: RwLock ( id ) ,
648
+ BlockReason :: RwLock ,
629
649
None ,
630
650
callback ! (
631
651
@capture<' tcx> {
632
- id : RwLockId ,
652
+ rwlock_ref : RwLockRef ,
633
653
retval: Scalar ,
634
654
dest: MPlaceTy <' tcx>,
635
655
}
636
656
|this, unblock: UnblockKind | {
637
657
assert_eq!( unblock, UnblockKind :: Ready ) ;
638
- this. rwlock_writer_lock( id ) ;
658
+ this. rwlock_writer_lock( & rwlock_ref ) ;
639
659
this. write_scalar( retval, & dest) ?;
640
660
interp_ok( ( ) )
641
661
}
@@ -700,15 +720,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
700
720
}
701
721
// Try to acquire the mutex.
702
722
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
703
- this. condvar_reacquire_mutex( & mutex_ref, retval_succ, dest)
723
+ this. condvar_reacquire_mutex( mutex_ref, retval_succ, dest)
704
724
}
705
725
UnblockKind :: TimedOut => {
706
726
// We have to remove the waiter from the queue again.
707
727
let thread = this. active_thread( ) ;
708
728
let waiters = & mut this. machine. sync. condvars[ condvar] . waiters;
709
729
waiters. retain( |waiter| * waiter != thread) ;
710
730
// Now get back the lock.
711
- this. condvar_reacquire_mutex( & mutex_ref, retval_timeout, dest)
731
+ this. condvar_reacquire_mutex( mutex_ref, retval_timeout, dest)
712
732
}
713
733
}
714
734
}
0 commit comments