@@ -75,8 +75,6 @@ impl VisitProvenance for MutexRef {
75
75
}
76
76
}
77
77
78
- declare_id ! ( RwLockId ) ;
79
-
80
78
/// The read-write lock state.
81
79
#[ derive( Default , Debug ) ]
82
80
struct RwLock {
@@ -111,6 +109,21 @@ struct RwLock {
111
109
clock_current_readers : VClock ,
112
110
}
113
111
112
+ #[ derive( Default , Clone , Debug ) ]
113
+ pub struct RwLockRef ( Rc < RefCell < RwLock > > ) ;
114
+
115
+ impl RwLockRef {
116
+ fn new ( ) -> Self {
117
+ RwLockRef ( Rc :: new ( RefCell :: new ( RwLock :: default ( ) ) ) )
118
+ }
119
+ }
120
+
121
+ impl VisitProvenance for RwLockRef {
122
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
123
+ // RwLockRef contains no provenance.
124
+ }
125
+ }
126
+
114
127
declare_id ! ( CondvarId ) ;
115
128
116
129
/// The conditional variable state.
@@ -164,7 +177,6 @@ struct FutexWaiter {
164
177
/// The state of all synchronization objects.
165
178
#[ derive( Default , Debug ) ]
166
179
pub struct SynchronizationObjects {
167
- rwlocks : IndexVec < RwLockId , RwLock > ,
168
180
condvars : IndexVec < CondvarId , Condvar > ,
169
181
pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
170
182
}
@@ -196,8 +208,8 @@ impl SynchronizationObjects {
196
208
pub fn mutex_create ( & mut self ) -> MutexRef {
197
209
MutexRef :: new ( )
198
210
}
199
- pub fn rwlock_create ( & mut self ) -> RwLockId {
200
- self . rwlocks . push ( Default :: default ( ) )
211
+ pub fn rwlock_create ( & mut self ) -> RwLockRef {
212
+ RwLockRef :: new ( )
201
213
}
202
214
203
215
pub fn condvar_create ( & mut self ) -> CondvarId {
@@ -447,12 +459,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
447
459
448
460
#[ inline]
449
461
/// 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] ;
462
+ fn rwlock_is_locked ( & self , rw_lock_ref : & RwLockRef ) -> bool {
463
+ let rwlock = rw_lock_ref. 0 . borrow ( ) ;
453
464
trace ! (
454
- "rwlock_is_locked: {:?} writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)" ,
455
- id,
465
+ "rwlock_is_locked: writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)" ,
456
466
rwlock. writer,
457
467
rwlock. readers. len( ) ,
458
468
) ;
@@ -461,21 +471,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
461
471
462
472
/// Check if write locked.
463
473
#[ 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) ;
474
+ fn rwlock_is_write_locked ( & self , rwlock_ref : & RwLockRef ) -> bool {
475
+ let rwlock = rwlock_ref. 0 . borrow ( ) ;
476
+ trace ! ( "rwlock_is_write_locked: writer is {:?}" , rwlock. writer) ;
468
477
rwlock. writer . is_some ( )
469
478
}
470
479
471
480
/// Read-lock the lock by adding the `reader` the list of threads that own
472
481
/// this lock.
473
- fn rwlock_reader_lock ( & mut self , id : RwLockId ) {
482
+ fn rwlock_reader_lock ( & mut self , rwlock_ref : & RwLockRef ) {
474
483
let this = self . eval_context_mut ( ) ;
475
484
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 ] ;
485
+ assert ! ( !this. rwlock_is_write_locked( rwlock_ref ) , "the lock is write locked" ) ;
486
+ trace ! ( "rwlock_reader_lock: now also held (one more time) by {:?}" , thread) ;
487
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
479
488
let count = rwlock. readers . entry ( thread) . or_insert ( 0 ) ;
480
489
* count = count. strict_add ( 1 ) ;
481
490
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
@@ -485,20 +494,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485
494
486
495
/// Try read-unlock the lock for the current threads and potentially give the lock to a new owner.
487
496
/// 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 > {
497
+ fn rwlock_reader_unlock ( & mut self , rwlock_ref : & RwLockRef ) -> InterpResult < ' tcx , bool > {
489
498
let this = self . eval_context_mut ( ) ;
490
499
let thread = this. active_thread ( ) ;
491
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
500
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
492
501
match rwlock. readers . entry ( thread) {
493
502
Entry :: Occupied ( mut entry) => {
494
503
let count = entry. get_mut ( ) ;
495
504
assert ! ( * count > 0 , "rwlock locked with count == 0" ) ;
496
505
* count -= 1 ;
497
506
if * count == 0 {
498
- trace ! ( "rwlock_reader_unlock: {:?} no longer held by {:?}" , id , thread) ;
507
+ trace ! ( "rwlock_reader_unlock: no longer held by {:?}" , thread) ;
499
508
entry. remove ( ) ;
500
509
} else {
501
- trace ! ( "rwlock_reader_unlock: {:?} held one less time by {:?}" , id , thread) ;
510
+ trace ! ( "rwlock_reader_unlock: held one less time by {:?}" , thread) ;
502
511
}
503
512
}
504
513
Entry :: Vacant ( _) => return interp_ok ( false ) , // we did not even own this lock
@@ -509,17 +518,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
509
518
rwlock. clock_current_readers . join ( clock)
510
519
} ) ;
511
520
}
521
+ drop ( rwlock) ; // FIXME can we avoid releasing and re-acquiring the RefCell?
512
522
513
523
// 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 ( ) {
524
+ if this. rwlock_is_locked ( rwlock_ref ) . not ( ) {
515
525
// All the readers are finished, so set the writer data-race handle to the value
516
526
// of the union of all reader data race handles, since the set of readers
517
527
// happen-before the writers
518
- let rwlock = & mut this. machine . sync . rwlocks [ id] ;
519
- rwlock. clock_unlocked . clone_from ( & rwlock. clock_current_readers ) ;
528
+ let mut rwlock = rwlock_ref. 0 . borrow_mut ( ) ;
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,29 @@ 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
+ assert ! (
552
+ this. rwlock_is_write_locked( & rwlock_ref) ,
553
+ "read-queueing on not write locked rwlock"
554
+ ) ;
555
+ rwlock_ref. 0 . borrow_mut ( ) . reader_queue . push_back ( thread) ;
541
556
this. block_thread (
542
- BlockReason :: RwLock ( id ) ,
557
+ BlockReason :: RwLock ,
543
558
None ,
544
559
callback ! (
545
560
@capture<' tcx> {
546
- id : RwLockId ,
561
+ rwlock_ref : RwLockRef ,
547
562
retval: Scalar ,
548
563
dest: MPlaceTy <' tcx>,
549
564
}
550
565
|this, unblock: UnblockKind | {
551
566
assert_eq!( unblock, UnblockKind :: Ready ) ;
552
- this. rwlock_reader_lock( id ) ;
567
+ this. rwlock_reader_lock( & rwlock_ref ) ;
553
568
this. write_scalar( retval, & dest) ?;
554
569
interp_ok( ( ) )
555
570
}
@@ -559,12 +574,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
559
574
560
575
/// Lock by setting the writer that owns the lock.
561
576
#[ inline]
562
- fn rwlock_writer_lock ( & mut self , id : RwLockId ) {
577
+ fn rwlock_writer_lock ( & mut self , rwlock_ref : & RwLockRef ) {
563
578
let this = self . eval_context_mut ( ) ;
564
579
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] ;
580
+ assert ! ( !this. rwlock_is_locked( rwlock_ref) , "the rwlock is already locked" ) ;
581
+ trace ! ( "rwlock_writer_lock: now held by {:?}" , thread) ;
582
+
583
+ let mut rwlock = rwlock_ref. 0 . borrow_mut ( ) ;
568
584
rwlock. writer = Some ( thread) ;
569
585
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
570
586
data_race. acquire_clock ( & rwlock. clock_unlocked , & this. machine . threads ) ;
@@ -574,35 +590,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
574
590
/// Try to unlock an rwlock held by the current thread.
575
591
/// Return `false` if it is held by another thread.
576
592
#[ inline]
577
- fn rwlock_writer_unlock ( & mut self , id : RwLockId ) -> InterpResult < ' tcx , bool > {
593
+ fn rwlock_writer_unlock ( & mut self , rwlock_ref : & RwLockRef ) -> InterpResult < ' tcx , bool > {
578
594
let this = self . eval_context_mut ( ) ;
579
595
let thread = this. active_thread ( ) ;
580
- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
596
+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
581
597
interp_ok ( if let Some ( current_writer) = rwlock. writer {
582
598
if current_writer != thread {
583
599
// Only the owner can unlock the rwlock.
584
600
return interp_ok ( false ) ;
585
601
}
586
602
rwlock. writer = None ;
587
- trace ! ( "rwlock_writer_unlock: {:?} unlocked by {:?}" , id , thread) ;
603
+ trace ! ( "rwlock_writer_unlock: unlocked by {:?}" , thread) ;
588
604
// Record release clock for next lock holder.
589
605
if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
590
606
data_race. release_clock ( & this. machine . threads , |clock| {
591
607
rwlock. clock_unlocked . clone_from ( clock)
592
608
} ) ;
593
609
}
610
+
594
611
// The thread was a writer.
595
612
//
596
613
// We are prioritizing writers here against the readers. As a
597
614
// result, not only readers can starve writers, but also writers can
598
615
// starve readers.
599
616
if let Some ( writer) = rwlock. writer_queue . pop_front ( ) {
600
- this. unblock_thread ( writer, BlockReason :: RwLock ( id) ) ?;
617
+ drop ( rwlock) ; // make RefCell available for unblock callback
618
+ this. unblock_thread ( writer, BlockReason :: RwLock ) ?;
601
619
} else {
602
620
// Take the entire read queue and wake them all up.
603
621
let readers = std:: mem:: take ( & mut rwlock. reader_queue ) ;
622
+ drop ( rwlock) ; // make RefCell available for unblock callback
604
623
for reader in readers {
605
- this. unblock_thread ( reader, BlockReason :: RwLock ( id ) ) ?;
624
+ this. unblock_thread ( reader, BlockReason :: RwLock ) ?;
606
625
}
607
626
}
608
627
true
@@ -616,26 +635,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
616
635
#[ inline]
617
636
fn rwlock_enqueue_and_block_writer (
618
637
& mut self ,
619
- id : RwLockId ,
638
+ rwlock_ref : RwLockRef ,
620
639
retval : Scalar ,
621
640
dest : MPlaceTy < ' tcx > ,
622
641
) {
623
642
let this = self . eval_context_mut ( ) ;
624
- assert ! ( this. rwlock_is_locked( id ) , "write-queueing on unlocked rwlock" ) ;
643
+ assert ! ( this. rwlock_is_locked( & rwlock_ref ) , "write-queueing on unlocked rwlock" ) ;
625
644
let thread = this. active_thread ( ) ;
626
- this . machine . sync . rwlocks [ id ] . writer_queue . push_back ( thread) ;
645
+ rwlock_ref . 0 . borrow_mut ( ) . writer_queue . push_back ( thread) ;
627
646
this. block_thread (
628
- BlockReason :: RwLock ( id ) ,
647
+ BlockReason :: RwLock ,
629
648
None ,
630
649
callback ! (
631
650
@capture<' tcx> {
632
- id : RwLockId ,
651
+ rwlock_ref : RwLockRef ,
633
652
retval: Scalar ,
634
653
dest: MPlaceTy <' tcx>,
635
654
}
636
655
|this, unblock: UnblockKind | {
637
656
assert_eq!( unblock, UnblockKind :: Ready ) ;
638
- this. rwlock_writer_lock( id ) ;
657
+ this. rwlock_writer_lock( & rwlock_ref ) ;
639
658
this. write_scalar( retval, & dest) ?;
640
659
interp_ok( ( ) )
641
660
}
0 commit comments