Merge pull request #4411 from LorrensP-2158466/remove-leaky-syncobj
Remove leaky synchronisation objects.
This commit is contained in:
commit
0ca067190b
8 changed files with 148 additions and 184 deletions
|
|
@ -1,13 +1,11 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use rustc_index::Idx;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::thread::DynUnblockCallback;
|
||||
use super::vector_clock::VClock;
|
||||
use crate::*;
|
||||
|
||||
super::sync::declare_id!(InitOnceId);
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
/// The current status of a one time initialization.
|
||||
pub enum InitOnceStatus {
|
||||
|
|
@ -25,44 +23,70 @@ pub(super) struct InitOnce {
|
|||
clock: VClock,
|
||||
}
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
impl InitOnce {
|
||||
#[inline]
|
||||
fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus {
|
||||
let this = self.eval_context_ref();
|
||||
this.machine.sync.init_onces[id].status
|
||||
}
|
||||
|
||||
/// Put the thread into the queue waiting for the initialization.
|
||||
#[inline]
|
||||
fn init_once_enqueue_and_block(&mut self, id: InitOnceId, callback: DynUnblockCallback<'tcx>) {
|
||||
let this = self.eval_context_mut();
|
||||
let thread = this.active_thread();
|
||||
let init_once = &mut this.machine.sync.init_onces[id];
|
||||
assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once");
|
||||
init_once.waiters.push_back(thread);
|
||||
this.block_thread(BlockReason::InitOnce(id), None, callback);
|
||||
pub fn status(&self) -> InitOnceStatus {
|
||||
self.status
|
||||
}
|
||||
|
||||
/// Begin initializing this InitOnce. Must only be called after checking that it is currently
|
||||
/// uninitialized.
|
||||
#[inline]
|
||||
fn init_once_begin(&mut self, id: InitOnceId) {
|
||||
let this = self.eval_context_mut();
|
||||
let init_once = &mut this.machine.sync.init_onces[id];
|
||||
pub fn begin(&mut self) {
|
||||
assert_eq!(
|
||||
init_once.status,
|
||||
self.status(),
|
||||
InitOnceStatus::Uninitialized,
|
||||
"beginning already begun or complete init once"
|
||||
);
|
||||
init_once.status = InitOnceStatus::Begun;
|
||||
self.status = InitOnceStatus::Begun;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct InitOnceRef(Rc<RefCell<InitOnce>>);
|
||||
|
||||
impl InitOnceRef {
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
pub fn status(&self) -> InitOnceStatus {
|
||||
self.0.borrow().status()
|
||||
}
|
||||
|
||||
pub fn begin(&self) {
|
||||
self.0.borrow_mut().begin();
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitProvenance for InitOnceRef {
|
||||
// InitOnce contains no provenance.
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Put the thread into the queue waiting for the initialization.
|
||||
#[inline]
|
||||
fn init_once_enqueue_and_block(
|
||||
&mut self,
|
||||
init_once_ref: InitOnceRef,
|
||||
callback: DynUnblockCallback<'tcx>,
|
||||
) {
|
||||
let this = self.eval_context_mut();
|
||||
let thread = this.active_thread();
|
||||
let mut init_once = init_once_ref.0.borrow_mut();
|
||||
assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once");
|
||||
|
||||
init_once.waiters.push_back(thread);
|
||||
this.block_thread(BlockReason::InitOnce, None, callback);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
|
||||
fn init_once_complete(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let init_once = &mut this.machine.sync.init_onces[id];
|
||||
|
||||
let mut init_once = init_once_ref.0.borrow_mut();
|
||||
assert_eq!(
|
||||
init_once.status,
|
||||
InitOnceStatus::Begun,
|
||||
|
|
@ -79,17 +103,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
// Wake up everyone.
|
||||
// need to take the queue to avoid having `this` be borrowed multiple times
|
||||
for waiter in std::mem::take(&mut init_once.waiters) {
|
||||
this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
|
||||
let waiters = std::mem::take(&mut init_once.waiters);
|
||||
drop(init_once);
|
||||
for waiter in waiters {
|
||||
this.unblock_thread(waiter, BlockReason::InitOnce)?;
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
|
||||
fn init_once_fail(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let init_once = &mut this.machine.sync.init_onces[id];
|
||||
let mut init_once = init_once_ref.0.borrow_mut();
|
||||
assert_eq!(
|
||||
init_once.status,
|
||||
InitOnceStatus::Begun,
|
||||
|
|
@ -106,7 +132,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
// Wake up one waiting thread, so they can go ahead and try to init this.
|
||||
if let Some(waiter) = init_once.waiters.pop_front() {
|
||||
this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
|
||||
drop(init_once);
|
||||
this.unblock_thread(waiter, BlockReason::InitOnce)?;
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
|
|
@ -115,15 +142,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
/// Synchronize with the previous completion of an InitOnce.
|
||||
/// Must only be called after checking that it is complete.
|
||||
#[inline]
|
||||
fn init_once_observe_completed(&mut self, id: InitOnceId) {
|
||||
fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) {
|
||||
let this = self.eval_context_mut();
|
||||
let init_once = init_once_ref.0.borrow();
|
||||
|
||||
assert_eq!(
|
||||
this.init_once_status(id),
|
||||
init_once.status,
|
||||
InitOnceStatus::Complete,
|
||||
"observing the completion of incomplete init once"
|
||||
);
|
||||
|
||||
this.acquire_clock(&this.machine.sync.init_onces[id].clock);
|
||||
this.acquire_clock(&init_once.clock);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,45 +8,10 @@ use std::time::Duration;
|
|||
|
||||
use rustc_abi::Size;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
|
||||
use super::init_once::InitOnce;
|
||||
use super::vector_clock::VClock;
|
||||
use crate::*;
|
||||
|
||||
/// We cannot use the `newtype_index!` macro because we have to use 0 as a
|
||||
/// sentinel value meaning that the identifier is not assigned. This is because
|
||||
/// the pthreads static initializers initialize memory with zeros (see the
|
||||
/// `src/shims/sync.rs` file).
|
||||
macro_rules! declare_id {
|
||||
($name: ident) => {
|
||||
/// 0 is used to indicate that the id was not yet assigned and,
|
||||
/// therefore, is not a valid identifier.
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
pub struct $name(std::num::NonZero<u32>);
|
||||
|
||||
impl $crate::VisitProvenance for $name {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
impl Idx for $name {
|
||||
fn new(idx: usize) -> Self {
|
||||
// We use 0 as a sentinel value (see the comment above) and,
|
||||
// therefore, need to shift by one when converting from an index
|
||||
// into a vector.
|
||||
let shifted_idx = u32::try_from(idx).unwrap().strict_add(1);
|
||||
$name(std::num::NonZero::new(shifted_idx).unwrap())
|
||||
}
|
||||
fn index(self) -> usize {
|
||||
// See the comment in `Self::new`.
|
||||
// (This cannot underflow because `self.0` is `NonZero<u32>`.)
|
||||
usize::try_from(self.0.get() - 1).unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(super) use declare_id;
|
||||
|
||||
/// The mutex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Mutex {
|
||||
|
|
@ -64,8 +29,8 @@ struct Mutex {
|
|||
pub struct MutexRef(Rc<RefCell<Mutex>>);
|
||||
|
||||
impl MutexRef {
|
||||
fn new() -> Self {
|
||||
MutexRef(Rc::new(RefCell::new(Mutex::default())))
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
/// Get the id of the thread that currently owns this lock, or `None` if it is not locked.
|
||||
|
|
@ -75,9 +40,8 @@ impl MutexRef {
|
|||
}
|
||||
|
||||
impl VisitProvenance for MutexRef {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
// Mutex contains no provenance.
|
||||
}
|
||||
// Mutex contains no provenance.
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
/// The read-write lock state.
|
||||
|
|
@ -138,8 +102,8 @@ impl RwLock {
|
|||
pub struct RwLockRef(Rc<RefCell<RwLock>>);
|
||||
|
||||
impl RwLockRef {
|
||||
fn new() -> Self {
|
||||
RwLockRef(Rc::new(RefCell::new(RwLock::default())))
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
pub fn is_locked(&self) -> bool {
|
||||
|
|
@ -152,13 +116,10 @@ impl RwLockRef {
|
|||
}
|
||||
|
||||
impl VisitProvenance for RwLockRef {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
// RwLockRef contains no provenance.
|
||||
}
|
||||
// RwLock contains no provenance.
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
declare_id!(CondvarId);
|
||||
|
||||
/// The conditional variable state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Condvar {
|
||||
|
|
@ -171,6 +132,24 @@ struct Condvar {
|
|||
clock: VClock,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct CondvarRef(Rc<RefCell<Condvar>>);
|
||||
|
||||
impl CondvarRef {
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
pub fn is_awaited(&self) -> bool {
|
||||
!self.0.borrow().waiters.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitProvenance for CondvarRef {
|
||||
// Condvar contains no provenance.
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
/// The futex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Futex {
|
||||
|
|
@ -183,19 +162,22 @@ struct Futex {
|
|||
clock: VClock,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct FutexRef(Rc<RefCell<Futex>>);
|
||||
|
||||
impl FutexRef {
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
|
||||
pub fn waiters(&self) -> usize {
|
||||
self.0.borrow().waiters.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitProvenance for FutexRef {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
// No provenance in `Futex`.
|
||||
}
|
||||
// Futex contains no provenance.
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
/// A thread waiting on a futex.
|
||||
|
|
@ -207,13 +189,6 @@ struct FutexWaiter {
|
|||
bitset: u32,
|
||||
}
|
||||
|
||||
/// The state of all synchronization objects.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct SynchronizationObjects {
|
||||
condvars: IndexVec<CondvarId, Condvar>,
|
||||
pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
|
||||
}
|
||||
|
||||
// Private extension trait for local helper methods
|
||||
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
|
@ -237,23 +212,6 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl SynchronizationObjects {
|
||||
pub fn mutex_create(&mut self) -> MutexRef {
|
||||
MutexRef::new()
|
||||
}
|
||||
pub fn rwlock_create(&mut self) -> RwLockRef {
|
||||
RwLockRef::new()
|
||||
}
|
||||
|
||||
pub fn condvar_create(&mut self) -> CondvarId {
|
||||
self.condvars.push(Default::default())
|
||||
}
|
||||
|
||||
pub fn init_once_create(&mut self) -> InitOnceId {
|
||||
self.init_onces.push(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> AllocExtra<'tcx> {
|
||||
fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
|
||||
self.sync.get(&offset).and_then(|s| s.downcast_ref::<T>())
|
||||
|
|
@ -663,19 +621,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
/// Is the conditional variable awaited?
|
||||
#[inline]
|
||||
fn condvar_is_awaited(&mut self, id: CondvarId) -> bool {
|
||||
let this = self.eval_context_mut();
|
||||
!this.machine.sync.condvars[id].waiters.is_empty()
|
||||
}
|
||||
|
||||
/// Release the mutex and let the current thread wait on the given condition variable.
|
||||
/// Once it is signaled, the mutex will be acquired and `retval_succ` will be written to `dest`.
|
||||
/// If the timeout happens first, `retval_timeout` will be written to `dest`.
|
||||
fn condvar_wait(
|
||||
&mut self,
|
||||
condvar: CondvarId,
|
||||
condvar_ref: CondvarRef,
|
||||
mutex_ref: MutexRef,
|
||||
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
|
||||
retval_succ: Scalar,
|
||||
|
|
@ -695,14 +646,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
);
|
||||
}
|
||||
let thread = this.active_thread();
|
||||
let waiters = &mut this.machine.sync.condvars[condvar].waiters;
|
||||
waiters.push_back(thread);
|
||||
|
||||
condvar_ref.0.borrow_mut().waiters.push_back(thread);
|
||||
this.block_thread(
|
||||
BlockReason::Condvar(condvar),
|
||||
BlockReason::Condvar,
|
||||
timeout,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
condvar: CondvarId,
|
||||
condvar_ref: CondvarRef,
|
||||
mutex_ref: MutexRef,
|
||||
retval_succ: Scalar,
|
||||
retval_timeout: Scalar,
|
||||
|
|
@ -714,7 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// The condvar was signaled. Make sure we get the clock for that.
|
||||
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
|
||||
data_race.acquire_clock(
|
||||
&this.machine.sync.condvars[condvar].clock,
|
||||
&condvar_ref.0.borrow().clock,
|
||||
&this.machine.threads,
|
||||
);
|
||||
}
|
||||
|
|
@ -725,7 +676,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
UnblockKind::TimedOut => {
|
||||
// We have to remove the waiter from the queue again.
|
||||
let thread = this.active_thread();
|
||||
let waiters = &mut this.machine.sync.condvars[condvar].waiters;
|
||||
let waiters = &mut condvar_ref.0.borrow_mut().waiters;
|
||||
waiters.retain(|waiter| *waiter != thread);
|
||||
// Now get back the lock.
|
||||
this.condvar_reacquire_mutex(mutex_ref, retval_timeout, dest)
|
||||
|
|
@ -739,9 +690,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
/// Wake up some thread (if there is any) sleeping on the conditional
|
||||
/// variable. Returns `true` iff any thread was woken up.
|
||||
fn condvar_signal(&mut self, id: CondvarId) -> InterpResult<'tcx, bool> {
|
||||
fn condvar_signal(&mut self, condvar_ref: &CondvarRef) -> InterpResult<'tcx, bool> {
|
||||
let this = self.eval_context_mut();
|
||||
let condvar = &mut this.machine.sync.condvars[id];
|
||||
let mut condvar = condvar_ref.0.borrow_mut();
|
||||
|
||||
// Each condvar signal happens-before the end of the condvar wake
|
||||
if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
|
||||
|
|
@ -750,7 +701,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let Some(waiter) = condvar.waiters.pop_front() else {
|
||||
return interp_ok(false);
|
||||
};
|
||||
this.unblock_thread(waiter, BlockReason::Condvar(id))?;
|
||||
drop(condvar);
|
||||
this.unblock_thread(waiter, BlockReason::Condvar)?;
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,13 +97,13 @@ pub enum BlockReason {
|
|||
/// Blocked on a mutex.
|
||||
Mutex,
|
||||
/// Blocked on a condition variable.
|
||||
Condvar(CondvarId),
|
||||
Condvar,
|
||||
/// Blocked on a reader-writer lock.
|
||||
RwLock,
|
||||
/// Blocked on a Futex variable.
|
||||
Futex,
|
||||
/// Blocked on an InitOnce.
|
||||
InitOnce(InitOnceId),
|
||||
InitOnce,
|
||||
/// Blocked on epoll.
|
||||
Epoll,
|
||||
/// Blocked on eventfd.
|
||||
|
|
|
|||
|
|
@ -128,10 +128,8 @@ pub use crate::concurrency::cpu_affinity::MAX_CPUS;
|
|||
pub use crate::concurrency::data_race::{
|
||||
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _,
|
||||
};
|
||||
pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
|
||||
pub use crate::concurrency::sync::{
|
||||
CondvarId, EvalContextExt as _, MutexRef, RwLockRef, SynchronizationObjects,
|
||||
};
|
||||
pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceRef};
|
||||
pub use crate::concurrency::sync::{CondvarRef, EvalContextExt as _, MutexRef, RwLockRef};
|
||||
pub use crate::concurrency::thread::{
|
||||
BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId,
|
||||
ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind,
|
||||
|
|
|
|||
|
|
@ -499,9 +499,6 @@ pub struct MiriMachine<'tcx> {
|
|||
/// in `sched_getaffinity`
|
||||
pub(crate) thread_cpu_affinity: FxHashMap<ThreadId, CpuAffinityMask>,
|
||||
|
||||
/// The state of the primitive synchronization objects.
|
||||
pub(crate) sync: SynchronizationObjects,
|
||||
|
||||
/// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
|
||||
pub(crate) layouts: PrimitiveLayouts<'tcx>,
|
||||
|
||||
|
|
@ -713,7 +710,6 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||
layouts,
|
||||
threads,
|
||||
thread_cpu_affinity,
|
||||
sync: SynchronizationObjects::default(),
|
||||
static_roots: Vec::new(),
|
||||
profiler,
|
||||
string_cache: Default::default(),
|
||||
|
|
@ -903,7 +899,6 @@ impl VisitProvenance for MiriMachine<'_> {
|
|||
let MiriMachine {
|
||||
threads,
|
||||
thread_cpu_affinity: _,
|
||||
sync: _,
|
||||
tls,
|
||||
env_vars,
|
||||
main_fn_ret_place,
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// LAZY_INIT_COOKIE). This can't be hit via `std::sync::Mutex`.
|
||||
interp_ok(MacOsUnfairLock::Poisoned)
|
||||
},
|
||||
|ecx| {
|
||||
let mutex_ref = ecx.machine.sync.mutex_create();
|
||||
interp_ok(MacOsUnfairLock::Active { mutex_ref })
|
||||
},
|
||||
|_| interp_ok(MacOsUnfairLock::Active { mutex_ref: MutexRef::new() }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,8 +171,7 @@ fn mutex_create<'tcx>(
|
|||
kind: MutexKind,
|
||||
) -> InterpResult<'tcx, PthreadMutex> {
|
||||
let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
|
||||
let id = ecx.machine.sync.mutex_create();
|
||||
let data = PthreadMutex { mutex_ref: id, kind };
|
||||
let data = PthreadMutex { mutex_ref: MutexRef::new(), kind };
|
||||
ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?;
|
||||
interp_ok(data)
|
||||
}
|
||||
|
|
@ -193,8 +192,7 @@ where
|
|||
|| throw_ub_format!("`pthread_mutex_t` can't be moved after first use"),
|
||||
|ecx| {
|
||||
let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
|
||||
let id = ecx.machine.sync.mutex_create();
|
||||
interp_ok(PthreadMutex { mutex_ref: id, kind })
|
||||
interp_ok(PthreadMutex { mutex_ref: MutexRef::new(), kind })
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -278,8 +276,7 @@ where
|
|||
)? {
|
||||
throw_unsup_format!("unsupported static initializer used for `pthread_rwlock_t`");
|
||||
}
|
||||
let rwlock_ref = ecx.machine.sync.rwlock_create();
|
||||
interp_ok(PthreadRwLock { rwlock_ref })
|
||||
interp_ok(PthreadRwLock { rwlock_ref: RwLockRef::new() })
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -372,9 +369,9 @@ enum ClockId {
|
|||
Monotonic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct PthreadCondvar {
|
||||
id: CondvarId,
|
||||
condvar_ref: CondvarRef,
|
||||
clock: ClockId,
|
||||
}
|
||||
|
||||
|
|
@ -384,9 +381,8 @@ fn cond_create<'tcx>(
|
|||
clock: ClockId,
|
||||
) -> InterpResult<'tcx, PthreadCondvar> {
|
||||
let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
|
||||
let id = ecx.machine.sync.condvar_create();
|
||||
let data = PthreadCondvar { id, clock };
|
||||
ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data)?;
|
||||
let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock };
|
||||
ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data.clone())?;
|
||||
interp_ok(data)
|
||||
}
|
||||
|
||||
|
|
@ -411,8 +407,7 @@ where
|
|||
throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`");
|
||||
}
|
||||
// This used the static initializer. The clock there is always CLOCK_REALTIME.
|
||||
let id = ecx.machine.sync.condvar_create();
|
||||
interp_ok(PthreadCondvar { id, clock: ClockId::Realtime })
|
||||
interp_ok(PthreadCondvar { condvar_ref: CondvarRef::new(), clock: ClockId::Realtime })
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -817,15 +812,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = cond_get_data(this, cond_op)?.id;
|
||||
this.condvar_signal(id)?;
|
||||
let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
|
||||
this.condvar_signal(&condvar)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = cond_get_data(this, cond_op)?.id;
|
||||
while this.condvar_signal(id)? {}
|
||||
let condvar = cond_get_data(this, cond_op)?.condvar_ref.clone();
|
||||
while this.condvar_signal(&condvar)? {}
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
|
|
@ -837,11 +832,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let data = *cond_get_data(this, cond_op)?;
|
||||
let data = cond_get_data(this, cond_op)?.clone();
|
||||
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
|
||||
|
||||
this.condvar_wait(
|
||||
data.id,
|
||||
data.condvar_ref,
|
||||
mutex_ref,
|
||||
None, // no timeout
|
||||
Scalar::from_i32(0),
|
||||
|
|
@ -861,7 +856,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let data = *cond_get_data(this, cond_op)?;
|
||||
let data = cond_get_data(this, cond_op)?.clone();
|
||||
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
|
||||
|
||||
// Extract the timeout.
|
||||
|
|
@ -884,7 +879,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
};
|
||||
|
||||
this.condvar_wait(
|
||||
data.id,
|
||||
data.condvar_ref,
|
||||
mutex_ref,
|
||||
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
|
||||
Scalar::from_i32(0),
|
||||
|
|
@ -900,8 +895,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
// Reading the field also has the side-effect that we detect double-`destroy`
|
||||
// since we make the field uninit below.
|
||||
let id = cond_get_data(this, cond_op)?.id;
|
||||
if this.condvar_is_awaited(id) {
|
||||
let condvar = &cond_get_data(this, cond_op)?.condvar_ref;
|
||||
if condvar.is_awaited() {
|
||||
throw_ub_format!("destroying an awaited conditional variable");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ use std::time::Duration;
|
|||
|
||||
use rustc_abi::Size;
|
||||
|
||||
use crate::concurrency::init_once::InitOnceStatus;
|
||||
use crate::concurrency::init_once::{EvalContextExt as _, InitOnceStatus};
|
||||
use crate::concurrency::sync::FutexRef;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Clone)]
|
||||
struct WindowsInitOnce {
|
||||
id: InitOnceId,
|
||||
init_once: InitOnceRef,
|
||||
}
|
||||
|
||||
struct WindowsFutex {
|
||||
|
|
@ -37,10 +37,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&init_once,
|
||||
init_offset,
|
||||
|| throw_ub_format!("`INIT_ONCE` can't be moved after first use"),
|
||||
|this| {
|
||||
|_| {
|
||||
// TODO: check that this is still all-zero.
|
||||
let id = this.machine.sync.init_once_create();
|
||||
interp_ok(WindowsInitOnce { id })
|
||||
interp_ok(WindowsInitOnce { init_once: InitOnceRef::new() })
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -48,20 +47,20 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
/// Returns `true` if we were succssful, `false` if we would block.
|
||||
fn init_once_try_begin(
|
||||
&mut self,
|
||||
id: InitOnceId,
|
||||
init_once_ref: &InitOnceRef,
|
||||
pending_place: &MPlaceTy<'tcx>,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx, bool> {
|
||||
let this = self.eval_context_mut();
|
||||
interp_ok(match this.init_once_status(id) {
|
||||
interp_ok(match init_once_ref.status() {
|
||||
InitOnceStatus::Uninitialized => {
|
||||
this.init_once_begin(id);
|
||||
init_once_ref.begin();
|
||||
this.write_scalar(this.eval_windows("c", "TRUE"), pending_place)?;
|
||||
this.write_scalar(this.eval_windows("c", "TRUE"), dest)?;
|
||||
true
|
||||
}
|
||||
InitOnceStatus::Complete => {
|
||||
this.init_once_observe_completed(id);
|
||||
this.init_once_observe_completed(init_once_ref);
|
||||
this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?;
|
||||
this.write_scalar(this.eval_windows("c", "TRUE"), dest)?;
|
||||
true
|
||||
|
|
@ -84,7 +83,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let id = this.init_once_get_data(init_once_op)?.id;
|
||||
let init_once = this.init_once_get_data(init_once_op)?.init_once.clone();
|
||||
let flags = this.read_scalar(flags_op)?.to_u32()?;
|
||||
// PBOOL is int*
|
||||
let pending_place = this.deref_pointer_as(pending_op, this.machine.layouts.i32)?;
|
||||
|
|
@ -98,7 +97,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
|
||||
}
|
||||
|
||||
if this.init_once_try_begin(id, &pending_place, dest)? {
|
||||
if this.init_once_try_begin(&init_once, &pending_place, dest)? {
|
||||
// Done!
|
||||
return interp_ok(());
|
||||
}
|
||||
|
|
@ -106,16 +105,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// We have to block, and then try again when we are woken up.
|
||||
let dest = dest.clone();
|
||||
this.init_once_enqueue_and_block(
|
||||
id,
|
||||
init_once.clone(),
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
id: InitOnceId,
|
||||
init_once: InitOnceRef,
|
||||
pending_place: MPlaceTy<'tcx>,
|
||||
dest: MPlaceTy<'tcx>,
|
||||
}
|
||||
|this, unblock: UnblockKind| {
|
||||
assert_eq!(unblock, UnblockKind::Ready);
|
||||
let ret = this.init_once_try_begin(id, &pending_place, &dest)?;
|
||||
let ret = this.init_once_try_begin(&init_once, &pending_place, &dest)?;
|
||||
assert!(ret, "we were woken up but init_once_try_begin still failed");
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
@ -132,7 +131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let id = this.init_once_get_data(init_once_op)?.id;
|
||||
let init_once = this.init_once_get_data(init_once_op)?.init_once.clone();
|
||||
let flags = this.read_scalar(flags_op)?.to_u32()?;
|
||||
let context = this.read_pointer(context_op)?;
|
||||
|
||||
|
|
@ -148,7 +147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
|
||||
}
|
||||
|
||||
if this.init_once_status(id) != InitOnceStatus::Begun {
|
||||
if init_once.status() != InitOnceStatus::Begun {
|
||||
// The docs do not say anything about this case, but it seems better to not allow it.
|
||||
throw_ub_format!(
|
||||
"calling InitOnceComplete on a one time initialization that has not begun or is already completed"
|
||||
|
|
@ -156,9 +155,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
|
||||
if success {
|
||||
this.init_once_complete(id)?;
|
||||
this.init_once_complete(&init_once)?;
|
||||
} else {
|
||||
this.init_once_fail(id)?;
|
||||
this.init_once_fail(&init_once)?;
|
||||
}
|
||||
|
||||
interp_ok(this.eval_windows("c", "TRUE"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue