Skip to content

Commit 479524f

Browse files
committed
mutex, rwlock: move some methods around so we borrow the RefCell less often
1 parent 74bd9df commit 479524f

File tree

3 files changed

+75
-80
lines changed

3 files changed

+75
-80
lines changed

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

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ impl MutexRef {
6767
fn new() -> Self {
6868
MutexRef(Rc::new(RefCell::new(Mutex::default())))
6969
}
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+
}
7075
}
7176

7277
impl VisitProvenance for MutexRef {
@@ -109,13 +114,41 @@ struct RwLock {
109114
clock_current_readers: VClock,
110115
}
111116

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+
112137
#[derive(Default, Clone, Debug)]
113138
pub struct RwLockRef(Rc<RefCell<RwLock>>);
114139

115140
impl RwLockRef {
116141
fn new() -> Self {
117142
RwLockRef(Rc::new(RefCell::new(RwLock::default())))
118143
}
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+
}
119152
}
120153

121154
impl VisitProvenance for RwLockRef {
@@ -186,17 +219,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
186219
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
187220
fn condvar_reacquire_mutex(
188221
&mut self,
189-
mutex_ref: &MutexRef,
222+
mutex_ref: MutexRef,
190223
retval: Scalar,
191224
dest: MPlaceTy<'tcx>,
192225
) -> InterpResult<'tcx> {
193226
let this = self.eval_context_mut();
194-
if this.mutex_is_locked(mutex_ref) {
195-
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());
196229
this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
197230
} else {
198231
// We can have it right now!
199-
this.mutex_lock(mutex_ref);
232+
this.mutex_lock(&mutex_ref);
200233
// Don't forget to write the return value.
201234
this.write_scalar(retval, &dest)?;
202235
}
@@ -346,18 +379,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
346379
Some(alloc_extra.get_sync::<T>(offset).unwrap())
347380
}
348381

349-
#[inline]
350-
/// Get the id of the thread that currently owns this lock.
351-
fn mutex_get_owner(&self, mutex_ref: &MutexRef) -> ThreadId {
352-
mutex_ref.0.borrow().owner.unwrap()
353-
}
354-
355-
#[inline]
356-
/// Check if locked.
357-
fn mutex_is_locked(&self, mutex_ref: &MutexRef) -> bool {
358-
mutex_ref.0.borrow().owner.is_some()
359-
}
360-
361382
/// Lock by setting the mutex owner and increasing the lock count.
362383
fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
363384
let this = self.eval_context_mut();
@@ -425,14 +446,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
425446
#[inline]
426447
fn mutex_enqueue_and_block(
427448
&mut self,
428-
mutex_ref: &MutexRef,
449+
mutex_ref: MutexRef,
429450
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
430451
) {
431452
let this = self.eval_context_mut();
432-
assert!(this.mutex_is_locked(mutex_ref), "queuing on unlocked mutex");
433453
let thread = this.active_thread();
434-
mutex_ref.0.borrow_mut().queue.push_back(thread);
435-
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);
436458
this.block_thread(
437459
BlockReason::Mutex,
438460
None,
@@ -444,7 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
444466
|this, unblock: UnblockKind| {
445467
assert_eq!(unblock, UnblockKind::Ready);
446468

447-
assert!(!this.mutex_is_locked(&mutex_ref));
469+
assert!(mutex_ref.owner().is_none());
448470
this.mutex_lock(&mutex_ref);
449471

450472
if let Some((retval, dest)) = retval_dest {
@@ -457,34 +479,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
457479
);
458480
}
459481

460-
#[inline]
461-
/// Check if locked.
462-
fn rwlock_is_locked(&self, rw_lock_ref: &RwLockRef) -> bool {
463-
let rwlock = rw_lock_ref.0.borrow();
464-
trace!(
465-
"rwlock_is_locked: writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)",
466-
rwlock.writer,
467-
rwlock.readers.len(),
468-
);
469-
rwlock.writer.is_some() || rwlock.readers.is_empty().not()
470-
}
471-
472-
/// Check if write locked.
473-
#[inline]
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);
477-
rwlock.writer.is_some()
478-
}
479-
480482
/// Read-lock the lock by adding the `reader` the list of threads that own
481483
/// this lock.
482484
fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) {
483485
let this = self.eval_context_mut();
484486
let thread = this.active_thread();
485-
assert!(!this.rwlock_is_write_locked(rwlock_ref), "the lock is write locked");
486487
trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread);
487488
let mut rwlock = rwlock_ref.0.borrow_mut();
489+
assert!(!rwlock.is_write_locked(), "the lock is write locked");
488490
let count = rwlock.readers.entry(thread).or_insert(0);
489491
*count = count.strict_add(1);
490492
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
@@ -518,14 +520,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
518520
rwlock.clock_current_readers.join(clock)
519521
});
520522
}
521-
drop(rwlock); // FIXME can we avoid releasing and re-acquiring the RefCell?
522523

523524
// The thread was a reader. If the lock is not held any more, give it to a writer.
524-
if this.rwlock_is_locked(rwlock_ref).not() {
525+
if rwlock.is_locked().not() {
525526
// All the readers are finished, so set the writer data-race handle to the value
526527
// of the union of all reader data race handles, since the set of readers
527528
// happen-before the writers
528-
let mut rwlock = rwlock_ref.0.borrow_mut();
529529
let rwlock_ref = &mut *rwlock;
530530
rwlock_ref.clock_unlocked.clone_from(&rwlock_ref.clock_current_readers);
531531
// See if there is a thread to unblock.
@@ -548,11 +548,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
548548
) {
549549
let this = self.eval_context_mut();
550550
let thread = this.active_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);
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);
556555
this.block_thread(
557556
BlockReason::RwLock,
558557
None,
@@ -577,10 +576,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
577576
fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) {
578577
let this = self.eval_context_mut();
579578
let thread = this.active_thread();
580-
assert!(!this.rwlock_is_locked(rwlock_ref), "the rwlock is already locked");
581579
trace!("rwlock_writer_lock: now held by {:?}", thread);
582580

583581
let mut rwlock = rwlock_ref.0.borrow_mut();
582+
assert!(!rwlock.is_locked(), "the rwlock is already locked");
584583
rwlock.writer = Some(thread);
585584
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
586585
data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads);
@@ -640,9 +639,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
640639
dest: MPlaceTy<'tcx>,
641640
) {
642641
let this = self.eval_context_mut();
643-
assert!(this.rwlock_is_locked(&rwlock_ref), "write-queueing on unlocked rwlock");
644642
let thread = this.active_thread();
645-
rwlock_ref.0.borrow_mut().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);
646647
this.block_thread(
647648
BlockReason::RwLock,
648649
None,
@@ -719,15 +720,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
719720
}
720721
// Try to acquire the mutex.
721722
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
722-
this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest)
723+
this.condvar_reacquire_mutex(mutex_ref, retval_succ, dest)
723724
}
724725
UnblockKind::TimedOut => {
725726
// We have to remove the waiter from the queue again.
726727
let thread = this.active_thread();
727728
let waiters = &mut this.machine.sync.condvars[condvar].waiters;
728729
waiters.retain(|waiter| *waiter != thread);
729730
// Now get back the lock.
730-
this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest)
731+
this.condvar_reacquire_mutex(mutex_ref, retval_timeout, dest)
731732
}
732733
}
733734
}

src/tools/miri/src/shims/unix/macos/sync.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
289289
};
290290
let mutex_ref = mutex_ref.clone();
291291

292-
if this.mutex_is_locked(&mutex_ref) {
293-
if this.mutex_get_owner(&mutex_ref) == this.active_thread() {
292+
if let Some(owner) = mutex_ref.owner() {
293+
if owner == this.active_thread() {
294294
// Matching the current macOS implementation: abort on reentrant locking.
295295
throw_machine_stop!(TerminationInfo::Abort(
296296
"attempted to lock an os_unfair_lock that is already locked by the current thread".to_owned()
297297
));
298298
}
299299

300-
this.mutex_enqueue_and_block(&mutex_ref, None);
300+
this.mutex_enqueue_and_block(mutex_ref, None);
301301
} else {
302302
this.mutex_lock(&mutex_ref);
303303
}
@@ -319,7 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
319319
};
320320
let mutex_ref = mutex_ref.clone();
321321

322-
if this.mutex_is_locked(&mutex_ref) {
322+
if mutex_ref.owner().is_some() {
323323
// Contrary to the blocking lock function, this does not check for
324324
// reentrancy.
325325
this.write_scalar(Scalar::from_bool(false), dest)?;
@@ -350,9 +350,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
350350
));
351351
}
352352

353-
// If the lock is not locked by anyone now, it went quer.
353+
// If the lock is not locked by anyone now, it went quiet.
354354
// Reset to zero so that it can be moved and initialized again for the next phase.
355-
if !this.mutex_is_locked(&mutex_ref) {
355+
if mutex_ref.owner().is_none() {
356356
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
357357
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
358358
}
@@ -371,9 +371,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
371371
};
372372
let mutex_ref = mutex_ref.clone();
373373

374-
if !this.mutex_is_locked(&mutex_ref)
375-
|| this.mutex_get_owner(&mutex_ref) != this.active_thread()
376-
{
374+
if mutex_ref.owner().is_none_or(|o| o != this.active_thread()) {
377375
throw_machine_stop!(TerminationInfo::Abort(
378376
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
379377
));
@@ -393,17 +391,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
393391
};
394392
let mutex_ref = mutex_ref.clone();
395393

396-
if this.mutex_is_locked(&mutex_ref)
397-
&& this.mutex_get_owner(&mutex_ref) == this.active_thread()
398-
{
394+
if mutex_ref.owner().is_some_and(|o| o == this.active_thread()) {
399395
throw_machine_stop!(TerminationInfo::Abort(
400396
"called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned()
401397
));
402398
}
403399

404-
// If the lock is not locked by anyone now, it went quer.
400+
// If the lock is not locked by anyone now, it went quiet.
405401
// Reset to zero so that it can be moved and initialized again for the next phase.
406-
if !this.mutex_is_locked(&mutex_ref) {
402+
if mutex_ref.owner().is_none() {
407403
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
408404
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
409405
}

src/tools/miri/src/shims/unix/sync.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -504,11 +504,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
504504

505505
let mutex = mutex_get_data(this, mutex_op)?.clone();
506506

507-
let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
508-
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
507+
let ret = if let Some(owner_thread) = mutex.mutex_ref.owner() {
509508
if owner_thread != this.active_thread() {
510509
this.mutex_enqueue_and_block(
511-
&mutex.mutex_ref,
510+
mutex.mutex_ref,
512511
Some((Scalar::from_i32(0), dest.clone())),
513512
);
514513
return interp_ok(());
@@ -541,8 +540,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
541540

542541
let mutex = mutex_get_data(this, mutex_op)?.clone();
543542

544-
interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
545-
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
543+
interp_ok(Scalar::from_i32(if let Some(owner_thread) = mutex.mutex_ref.owner() {
546544
if owner_thread != this.active_thread() {
547545
this.eval_libc_i32("EBUSY")
548546
} else {
@@ -596,7 +594,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
596594
// since we make the field uninit below.
597595
let mutex = mutex_get_data(this, mutex_op)?.clone();
598596

599-
if this.mutex_is_locked(&mutex.mutex_ref) {
597+
if mutex.mutex_ref.owner().is_some() {
600598
throw_ub_format!("destroyed a locked mutex");
601599
}
602600

@@ -618,7 +616,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
618616

619617
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
620618

621-
if this.rwlock_is_write_locked(&rwlock.rwlock_ref) {
619+
if rwlock.rwlock_ref.is_write_locked() {
622620
this.rwlock_enqueue_and_block_reader(
623621
rwlock.rwlock_ref,
624622
Scalar::from_i32(0),
@@ -637,7 +635,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
637635

638636
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
639637

640-
if this.rwlock_is_write_locked(&rwlock.rwlock_ref) {
638+
if rwlock.rwlock_ref.is_write_locked() {
641639
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
642640
} else {
643641
this.rwlock_reader_lock(&rwlock.rwlock_ref);
@@ -654,7 +652,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
654652

655653
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
656654

657-
if this.rwlock_is_locked(&rwlock.rwlock_ref) {
655+
if rwlock.rwlock_ref.is_locked() {
658656
// Note: this will deadlock if the lock is already locked by this
659657
// thread in any way.
660658
//
@@ -685,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
685683

686684
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
687685

688-
if this.rwlock_is_locked(&rwlock.rwlock_ref) {
686+
if rwlock.rwlock_ref.is_locked() {
689687
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
690688
} else {
691689
this.rwlock_writer_lock(&rwlock.rwlock_ref);
@@ -714,7 +712,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
714712
// since we make the field uninit below.
715713
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
716714

717-
if this.rwlock_is_locked(&rwlock.rwlock_ref) {
715+
if rwlock.rwlock_ref.is_locked() {
718716
throw_ub_format!("destroyed a locked rwlock");
719717
}
720718

0 commit comments

Comments
 (0)