Skip to content

Commit 74bd9df

Browse files
use RwLockRef instead of RwLockId
1 parent 31e9806 commit 74bd9df

File tree

4 files changed

+101
-72
lines changed

4 files changed

+101
-72
lines changed

src/tools/miri/src/concurrency/sync.rs

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
8280
struct 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+
114127
declare_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)]
166179
pub 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
}

src/tools/miri/src/concurrency/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub enum BlockReason {
9999
/// Blocked on a condition variable.
100100
Condvar(CondvarId),
101101
/// Blocked on a reader-writer lock.
102-
RwLock(RwLockId),
102+
RwLock,
103103
/// Blocked on a Futex variable.
104104
Futex,
105105
/// Blocked on an InitOnce.

src/tools/miri/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub use crate::concurrency::data_race::{
122122
};
123123
pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
124124
pub use crate::concurrency::sync::{
125-
CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
125+
CondvarId, EvalContextExt as _, MutexRef, RwLockRef, SynchronizationObjects,
126126
};
127127
pub use crate::concurrency::thread::{
128128
BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId,

0 commit comments

Comments
 (0)