diff --git a/src/concurrency/init_once.rs b/src/concurrency/init_once.rs index c26384f65f..165215f927 100644 --- a/src/concurrency/init_once.rs +++ b/src/concurrency/init_once.rs @@ -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>); + +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_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> { + fn init_once_enqueue_and_block( + &mut self, + init_once_ref: InitOnceRef, + callback: DynUnblockCallback<'tcx>, + ) { let this = self.eval_context_mut(); - let init_once = &mut this.machine.sync.init_onces[id]; + 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, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + 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); } } diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index 74379d6438..179094db0f 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -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); - - 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`.) - 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>); 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>); 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>); + +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>); 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, - pub(super) init_onces: IndexVec, -} - // 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(&self, offset: Size) -> Option<&T> { self.sync.get(&offset).and_then(|s| s.downcast_ref::()) @@ -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) } diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index c8a408fd8a..56c1979485 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -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. diff --git a/src/lib.rs b/src/lib.rs index edbc004d05..494e0f3fe8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,10 +127,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, diff --git a/src/machine.rs b/src/machine.rs index 3a748c4c68..088de965e2 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -499,9 +499,6 @@ pub struct MiriMachine<'tcx> { /// in `sched_getaffinity` pub(crate) thread_cpu_affinity: FxHashMap, - /// 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, diff --git a/src/shims/unix/macos/sync.rs b/src/shims/unix/macos/sync.rs index 19f55e6c91..05616dd5a4 100644 --- a/src/shims/unix/macos/sync.rs +++ b/src/shims/unix/macos/sync.rs @@ -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() }), ) } } diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 50eb4d9228..e20e3b79c3 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -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"); } diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index 8d5ea7db9e..9165e76b63 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -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"))