Skip to content

Commit 7137683

Browse files
authored
Merge pull request rust-lang#4002 from tiif/leakythread
Remove MutexID list
2 parents e1d4c9e + bfb36e3 commit 7137683

File tree

5 files changed

+109
-86
lines changed

5 files changed

+109
-86
lines changed

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

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::cell::RefCell;
22
use std::collections::VecDeque;
33
use std::collections::hash_map::Entry;
4+
use std::default::Default;
45
use std::ops::Not;
56
use std::rc::Rc;
67
use std::time::Duration;
@@ -46,8 +47,6 @@ macro_rules! declare_id {
4647
}
4748
pub(super) use declare_id;
4849

49-
declare_id!(MutexId);
50-
5150
/// The mutex state.
5251
#[derive(Default, Debug)]
5352
struct Mutex {
@@ -61,6 +60,21 @@ struct Mutex {
6160
clock: VClock,
6261
}
6362

63+
#[derive(Default, Clone, Debug)]
64+
pub struct MutexRef(Rc<RefCell<Mutex>>);
65+
66+
impl MutexRef {
67+
fn new() -> Self {
68+
MutexRef(Rc::new(RefCell::new(Mutex::default())))
69+
}
70+
}
71+
72+
impl VisitProvenance for MutexRef {
73+
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
74+
// Mutex contains no provenance.
75+
}
76+
}
77+
6478
declare_id!(RwLockId);
6579

6680
/// The read-write lock state.
@@ -144,7 +158,6 @@ struct FutexWaiter {
144158
/// The state of all synchronization objects.
145159
#[derive(Default, Debug)]
146160
pub struct SynchronizationObjects {
147-
mutexes: IndexVec<MutexId, Mutex>,
148161
rwlocks: IndexVec<RwLockId, RwLock>,
149162
condvars: IndexVec<CondvarId, Condvar>,
150163
pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
@@ -155,17 +168,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
155168
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
156169
fn condvar_reacquire_mutex(
157170
&mut self,
158-
mutex: MutexId,
171+
mutex_ref: &MutexRef,
159172
retval: Scalar,
160173
dest: MPlaceTy<'tcx>,
161174
) -> InterpResult<'tcx> {
162175
let this = self.eval_context_mut();
163-
if this.mutex_is_locked(mutex) {
164-
assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
165-
this.mutex_enqueue_and_block(mutex, Some((retval, dest)));
176+
if this.mutex_is_locked(mutex_ref) {
177+
assert_ne!(this.mutex_get_owner(mutex_ref), this.active_thread());
178+
this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
166179
} else {
167180
// We can have it right now!
168-
this.mutex_lock(mutex);
181+
this.mutex_lock(mutex_ref);
169182
// Don't forget to write the return value.
170183
this.write_scalar(retval, &dest)?;
171184
}
@@ -174,10 +187,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
174187
}
175188

176189
impl SynchronizationObjects {
177-
pub fn mutex_create(&mut self) -> MutexId {
178-
self.mutexes.push(Default::default())
190+
pub fn mutex_create(&mut self) -> MutexRef {
191+
MutexRef::new()
179192
}
180-
181193
pub fn rwlock_create(&mut self) -> RwLockId {
182194
self.rwlocks.push(Default::default())
183195
}
@@ -209,7 +221,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
209221
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
210222
/// Helper for lazily initialized `alloc_extra.sync` data:
211223
/// this forces an immediate init.
212-
fn lazy_sync_init<T: 'static + Copy>(
224+
fn lazy_sync_init<T: 'static>(
213225
&mut self,
214226
primitive: &MPlaceTy<'tcx>,
215227
init_offset: Size,
@@ -235,7 +247,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235247
/// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
236248
/// and stores that in `alloc_extra.sync`.
237249
/// - Otherwise, calls `new_data` to initialize the primitive.
238-
fn lazy_sync_get_data<T: 'static + Copy>(
250+
///
251+
/// The return value is a *clone* of the stored data, so if you intend to mutate it
252+
/// better wrap everything into an `Rc`.
253+
fn lazy_sync_get_data<T: 'static + Clone>(
239254
&mut self,
240255
primitive: &MPlaceTy<'tcx>,
241256
init_offset: Size,
@@ -266,15 +281,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
266281
let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
267282
let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
268283
if let Some(data) = alloc_extra.get_sync::<T>(offset) {
269-
interp_ok(*data)
284+
interp_ok(data.clone())
270285
} else {
271286
let data = missing_data()?;
272-
alloc_extra.sync.insert(offset, Box::new(data));
287+
alloc_extra.sync.insert(offset, Box::new(data.clone()));
273288
interp_ok(data)
274289
}
275290
} else {
276291
let data = new_data(this)?;
277-
this.lazy_sync_init(primitive, init_offset, data)?;
292+
this.lazy_sync_init(primitive, init_offset, data.clone())?;
278293
interp_ok(data)
279294
}
280295
}
@@ -311,23 +326,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
311326

312327
#[inline]
313328
/// Get the id of the thread that currently owns this lock.
314-
fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
315-
let this = self.eval_context_ref();
316-
this.machine.sync.mutexes[id].owner.unwrap()
329+
fn mutex_get_owner(&mut self, mutex_ref: &MutexRef) -> ThreadId {
330+
mutex_ref.0.borrow().owner.unwrap()
317331
}
318332

319333
#[inline]
320334
/// Check if locked.
321-
fn mutex_is_locked(&self, id: MutexId) -> bool {
322-
let this = self.eval_context_ref();
323-
this.machine.sync.mutexes[id].owner.is_some()
335+
fn mutex_is_locked(&self, mutex_ref: &MutexRef) -> bool {
336+
mutex_ref.0.borrow().owner.is_some()
324337
}
325338

326339
/// Lock by setting the mutex owner and increasing the lock count.
327-
fn mutex_lock(&mut self, id: MutexId) {
340+
fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
328341
let this = self.eval_context_mut();
329342
let thread = this.active_thread();
330-
let mutex = &mut this.machine.sync.mutexes[id];
343+
let mut mutex = mutex_ref.0.borrow_mut();
331344
if let Some(current_owner) = mutex.owner {
332345
assert_eq!(thread, current_owner, "mutex already locked by another thread");
333346
assert!(
@@ -347,9 +360,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
347360
/// count. If the lock count reaches 0, release the lock and potentially
348361
/// give to a new owner. If the lock was not locked by the current thread,
349362
/// return `None`.
350-
fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<usize>> {
363+
fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
351364
let this = self.eval_context_mut();
352-
let mutex = &mut this.machine.sync.mutexes[id];
365+
let mut mutex = mutex_ref.0.borrow_mut();
353366
interp_ok(if let Some(current_owner) = mutex.owner {
354367
// Mutex is locked.
355368
if current_owner != this.machine.threads.active_thread() {
@@ -367,8 +380,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367380
mutex.clock.clone_from(clock)
368381
});
369382
}
370-
if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() {
371-
this.unblock_thread(thread, BlockReason::Mutex(id))?;
383+
let thread_id = mutex.queue.pop_front();
384+
// We need to drop our mutex borrow before unblock_thread
385+
// because it will be borrowed again in the unblock callback.
386+
drop(mutex);
387+
if thread_id.is_some() {
388+
this.unblock_thread(thread_id.unwrap(), BlockReason::Mutex)?;
372389
}
373390
}
374391
Some(old_lock_count)
@@ -385,24 +402,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385402
#[inline]
386403
fn mutex_enqueue_and_block(
387404
&mut self,
388-
id: MutexId,
405+
mutex_ref: &MutexRef,
389406
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
390407
) {
391408
let this = self.eval_context_mut();
392-
assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
409+
assert!(this.mutex_is_locked(mutex_ref), "queuing on unlocked mutex");
393410
let thread = this.active_thread();
394-
this.machine.sync.mutexes[id].queue.push_back(thread);
411+
mutex_ref.0.borrow_mut().queue.push_back(thread);
412+
let mutex_ref = mutex_ref.clone();
395413
this.block_thread(
396-
BlockReason::Mutex(id),
414+
BlockReason::Mutex,
397415
None,
398416
callback!(
399417
@capture<'tcx> {
400-
id: MutexId,
418+
mutex_ref: MutexRef,
401419
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
402420
}
403421
@unblock = |this| {
404-
assert!(!this.mutex_is_locked(id));
405-
this.mutex_lock(id);
422+
assert!(!this.mutex_is_locked(&mutex_ref));
423+
this.mutex_lock(&mutex_ref);
406424

407425
if let Some((retval, dest)) = retval_dest {
408426
this.write_scalar(retval, &dest)?;
@@ -623,14 +641,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
623641
fn condvar_wait(
624642
&mut self,
625643
condvar: CondvarId,
626-
mutex: MutexId,
644+
mutex_ref: MutexRef,
627645
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
628646
retval_succ: Scalar,
629647
retval_timeout: Scalar,
630648
dest: MPlaceTy<'tcx>,
631649
) -> InterpResult<'tcx> {
632650
let this = self.eval_context_mut();
633-
if let Some(old_locked_count) = this.mutex_unlock(mutex)? {
651+
if let Some(old_locked_count) = this.mutex_unlock(&mutex_ref)? {
634652
if old_locked_count != 1 {
635653
throw_unsup_format!(
636654
"awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -650,7 +668,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
650668
callback!(
651669
@capture<'tcx> {
652670
condvar: CondvarId,
653-
mutex: MutexId,
671+
mutex_ref: MutexRef,
654672
retval_succ: Scalar,
655673
retval_timeout: Scalar,
656674
dest: MPlaceTy<'tcx>,
@@ -665,15 +683,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
665683
}
666684
// Try to acquire the mutex.
667685
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
668-
this.condvar_reacquire_mutex(mutex, retval_succ, dest)
686+
this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest)
669687
}
670688
@timeout = |this| {
671689
// We have to remove the waiter from the queue again.
672690
let thread = this.active_thread();
673691
let waiters = &mut this.machine.sync.condvars[condvar].waiters;
674692
waiters.retain(|waiter| *waiter != thread);
675693
// Now get back the lock.
676-
this.condvar_reacquire_mutex(mutex, retval_timeout, dest)
694+
this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest)
677695
}
678696
),
679697
);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ pub enum BlockReason {
141141
/// Waiting for time to pass.
142142
Sleep,
143143
/// Blocked on a mutex.
144-
Mutex(MutexId),
144+
Mutex,
145145
/// Blocked on a condition variable.
146146
Condvar(CondvarId),
147147
/// Blocked on a reader-writer lock.

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 _, MutexId, RwLockId, SynchronizationObjects,
125+
CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
126126
};
127127
pub use crate::concurrency::thread::{
128128
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor,

0 commit comments

Comments
 (0)