@@ -75,8 +75,6 @@ impl VisitProvenance for MutexRef {
7575 }
7676}
7777
78- declare_id ! ( RwLockId ) ;
79-
8078/// The read-write lock state.
8179#[ derive( Default , Debug ) ]
8280struct RwLock {
@@ -111,6 +109,21 @@ struct RwLock {
111109 clock_current_readers : VClock ,
112110}
113111
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+
114127declare_id ! ( CondvarId ) ;
115128
116129/// The conditional variable state.
@@ -164,7 +177,6 @@ struct FutexWaiter {
164177/// The state of all synchronization objects.
165178#[ derive( Default , Debug ) ]
166179pub struct SynchronizationObjects {
167- rwlocks : IndexVec < RwLockId , RwLock > ,
168180 condvars : IndexVec < CondvarId , Condvar > ,
169181 pub ( super ) init_onces : IndexVec < InitOnceId , InitOnce > ,
170182}
@@ -196,8 +208,8 @@ impl SynchronizationObjects {
196208 pub fn mutex_create ( & mut self ) -> MutexRef {
197209 MutexRef :: new ( )
198210 }
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 ( )
201213 }
202214
203215 pub fn condvar_create ( & mut self ) -> CondvarId {
@@ -447,12 +459,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
447459
448460 #[ inline]
449461 /// 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 ( ) ;
453464 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)" ,
456466 rwlock. writer,
457467 rwlock. readers. len( ) ,
458468 ) ;
@@ -461,21 +471,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
461471
462472 /// Check if write locked.
463473 #[ 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) ;
468477 rwlock. writer . is_some ( )
469478 }
470479
471480 /// Read-lock the lock by adding the `reader` the list of threads that own
472481 /// this lock.
473- fn rwlock_reader_lock ( & mut self , id : RwLockId ) {
482+ fn rwlock_reader_lock ( & mut self , rwlock_ref : & RwLockRef ) {
474483 let this = self . eval_context_mut ( ) ;
475484 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 ( ) ;
479488 let count = rwlock. readers . entry ( thread) . or_insert ( 0 ) ;
480489 * count = count. strict_add ( 1 ) ;
481490 if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
@@ -485,20 +494,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485494
486495 /// Try read-unlock the lock for the current threads and potentially give the lock to a new owner.
487496 /// 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 > {
489498 let this = self . eval_context_mut ( ) ;
490499 let thread = this. active_thread ( ) ;
491- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
500+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
492501 match rwlock. readers . entry ( thread) {
493502 Entry :: Occupied ( mut entry) => {
494503 let count = entry. get_mut ( ) ;
495504 assert ! ( * count > 0 , "rwlock locked with count == 0" ) ;
496505 * count -= 1 ;
497506 if * count == 0 {
498- trace ! ( "rwlock_reader_unlock: {:?} no longer held by {:?}" , id , thread) ;
507+ trace ! ( "rwlock_reader_unlock: no longer held by {:?}" , thread) ;
499508 entry. remove ( ) ;
500509 } else {
501- trace ! ( "rwlock_reader_unlock: {:?} held one less time by {:?}" , id , thread) ;
510+ trace ! ( "rwlock_reader_unlock: held one less time by {:?}" , thread) ;
502511 }
503512 }
504513 Entry :: Vacant ( _) => return interp_ok ( false ) , // we did not even own this lock
@@ -509,17 +518,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
509518 rwlock. clock_current_readers . join ( clock)
510519 } ) ;
511520 }
521+ drop ( rwlock) ; // FIXME can we avoid releasing and re-acquiring the RefCell?
512522
513523 // 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 ( ) {
515525 // All the readers are finished, so set the writer data-race handle to the value
516526 // of the union of all reader data race handles, since the set of readers
517527 // 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 ) ;
520531 // 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 ) ?;
523535 }
524536 }
525537 interp_ok ( true )
@@ -530,26 +542,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
530542 #[ inline]
531543 fn rwlock_enqueue_and_block_reader (
532544 & mut self ,
533- id : RwLockId ,
545+ rwlock_ref : RwLockRef ,
534546 retval : Scalar ,
535547 dest : MPlaceTy < ' tcx > ,
536548 ) {
537549 let this = self . eval_context_mut ( ) ;
538550 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) ;
541556 this. block_thread (
542- BlockReason :: RwLock ( id ) ,
557+ BlockReason :: RwLock ,
543558 None ,
544559 callback ! (
545560 @capture<' tcx> {
546- id : RwLockId ,
561+ rwlock_ref : RwLockRef ,
547562 retval: Scalar ,
548563 dest: MPlaceTy <' tcx>,
549564 }
550565 |this, unblock: UnblockKind | {
551566 assert_eq!( unblock, UnblockKind :: Ready ) ;
552- this. rwlock_reader_lock( id ) ;
567+ this. rwlock_reader_lock( & rwlock_ref ) ;
553568 this. write_scalar( retval, & dest) ?;
554569 interp_ok( ( ) )
555570 }
@@ -559,12 +574,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
559574
560575 /// Lock by setting the writer that owns the lock.
561576 #[ inline]
562- fn rwlock_writer_lock ( & mut self , id : RwLockId ) {
577+ fn rwlock_writer_lock ( & mut self , rwlock_ref : & RwLockRef ) {
563578 let this = self . eval_context_mut ( ) ;
564579 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 ( ) ;
568584 rwlock. writer = Some ( thread) ;
569585 if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
570586 data_race. acquire_clock ( & rwlock. clock_unlocked , & this. machine . threads ) ;
@@ -574,35 +590,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
574590 /// Try to unlock an rwlock held by the current thread.
575591 /// Return `false` if it is held by another thread.
576592 #[ 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 > {
578594 let this = self . eval_context_mut ( ) ;
579595 let thread = this. active_thread ( ) ;
580- let rwlock = & mut this . machine . sync . rwlocks [ id ] ;
596+ let mut rwlock = rwlock_ref . 0 . borrow_mut ( ) ;
581597 interp_ok ( if let Some ( current_writer) = rwlock. writer {
582598 if current_writer != thread {
583599 // Only the owner can unlock the rwlock.
584600 return interp_ok ( false ) ;
585601 }
586602 rwlock. writer = None ;
587- trace ! ( "rwlock_writer_unlock: {:?} unlocked by {:?}" , id , thread) ;
603+ trace ! ( "rwlock_writer_unlock: unlocked by {:?}" , thread) ;
588604 // Record release clock for next lock holder.
589605 if let Some ( data_race) = this. machine . data_race . as_vclocks_ref ( ) {
590606 data_race. release_clock ( & this. machine . threads , |clock| {
591607 rwlock. clock_unlocked . clone_from ( clock)
592608 } ) ;
593609 }
610+
594611 // The thread was a writer.
595612 //
596613 // We are prioritizing writers here against the readers. As a
597614 // result, not only readers can starve writers, but also writers can
598615 // starve readers.
599616 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 ) ?;
601619 } else {
602620 // Take the entire read queue and wake them all up.
603621 let readers = std:: mem:: take ( & mut rwlock. reader_queue ) ;
622+ drop ( rwlock) ; // make RefCell available for unblock callback
604623 for reader in readers {
605- this. unblock_thread ( reader, BlockReason :: RwLock ( id ) ) ?;
624+ this. unblock_thread ( reader, BlockReason :: RwLock ) ?;
606625 }
607626 }
608627 true
@@ -616,26 +635,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
616635 #[ inline]
617636 fn rwlock_enqueue_and_block_writer (
618637 & mut self ,
619- id : RwLockId ,
638+ rwlock_ref : RwLockRef ,
620639 retval : Scalar ,
621640 dest : MPlaceTy < ' tcx > ,
622641 ) {
623642 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" ) ;
625644 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) ;
627646 this. block_thread (
628- BlockReason :: RwLock ( id ) ,
647+ BlockReason :: RwLock ,
629648 None ,
630649 callback ! (
631650 @capture<' tcx> {
632- id : RwLockId ,
651+ rwlock_ref : RwLockRef ,
633652 retval: Scalar ,
634653 dest: MPlaceTy <' tcx>,
635654 }
636655 |this, unblock: UnblockKind | {
637656 assert_eq!( unblock, UnblockKind :: Ready ) ;
638- this. rwlock_writer_lock( id ) ;
657+ this. rwlock_writer_lock( & rwlock_ref ) ;
639658 this. write_scalar( retval, & dest) ?;
640659 interp_ok( ( ) )
641660 }
0 commit comments