miri/concurrency/
init_once.rsuse std::collections::VecDeque;
use rustc_index::Idx;
use super::thread::DynUnblockCallback;
use super::vector_clock::VClock;
use crate::*;
super::sync::declare_id!(InitOnceId);
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub enum InitOnceStatus {
#[default]
Uninitialized,
Begun,
Complete,
}
#[derive(Default, Debug)]
pub(super) struct InitOnce {
status: InitOnceStatus,
waiters: VecDeque<ThreadId>,
clock: VClock,
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus {
let this = self.eval_context_ref();
this.machine.sync.init_onces[id].status
}
#[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);
}
#[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];
assert_eq!(
init_once.status,
InitOnceStatus::Uninitialized,
"beginning already begun or complete init once"
);
init_once.status = InitOnceStatus::Begun;
}
#[inline]
fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let init_once = &mut this.machine.sync.init_onces[id];
assert_eq!(
init_once.status,
InitOnceStatus::Begun,
"completing already complete or uninit init once"
);
init_once.status = InitOnceStatus::Complete;
if let Some(data_race) = &this.machine.data_race {
data_race
.release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
}
for waiter in std::mem::take(&mut init_once.waiters) {
this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
}
interp_ok(())
}
#[inline]
fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let init_once = &mut this.machine.sync.init_onces[id];
assert_eq!(
init_once.status,
InitOnceStatus::Begun,
"failing already completed or uninit init once"
);
init_once.status = InitOnceStatus::Uninitialized;
if let Some(data_race) = &this.machine.data_race {
data_race
.release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
}
if let Some(waiter) = init_once.waiters.pop_front() {
this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
}
interp_ok(())
}
#[inline]
fn init_once_observe_completed(&mut self, id: InitOnceId) {
let this = self.eval_context_mut();
assert_eq!(
this.init_once_status(id),
InitOnceStatus::Complete,
"observing the completion of incomplete init once"
);
this.acquire_clock(&this.machine.sync.init_onces[id].clock);
}
}