1use std::collections::VecDeque;
23use rustc_index::Idx;
45use super::thread::DynUnblockCallback;
6use super::vector_clock::VClock;
7use crate::*;
89super::sync::declare_id!(InitOnceId);
1011#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
12/// The current status of a one time initialization.
13pub enum InitOnceStatus {
14#[default]
15Uninitialized,
16 Begun,
17 Complete,
18}
1920/// The one time initialization state.
21#[derive(Default, Debug)]
22pub(super) struct InitOnce {
23 status: InitOnceStatus,
24 waiters: VecDeque<ThreadId>,
25 clock: VClock,
26}
2728impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
29pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
30#[inline]
31fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus {
32let this = self.eval_context_ref();
33 this.machine.sync.init_onces[id].status
34 }
3536/// Put the thread into the queue waiting for the initialization.
37#[inline]
38fn init_once_enqueue_and_block(&mut self, id: InitOnceId, callback: DynUnblockCallback<'tcx>) {
39let this = self.eval_context_mut();
40let thread = this.active_thread();
41let init_once = &mut this.machine.sync.init_onces[id];
42assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once");
43 init_once.waiters.push_back(thread);
44 this.block_thread(BlockReason::InitOnce(id), None, callback);
45 }
4647/// Begin initializing this InitOnce. Must only be called after checking that it is currently
48 /// uninitialized.
49#[inline]
50fn init_once_begin(&mut self, id: InitOnceId) {
51let this = self.eval_context_mut();
52let init_once = &mut this.machine.sync.init_onces[id];
53assert_eq!(
54 init_once.status,
55 InitOnceStatus::Uninitialized,
56"beginning already begun or complete init once"
57);
58 init_once.status = InitOnceStatus::Begun;
59 }
6061#[inline]
62fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
63let this = self.eval_context_mut();
64let init_once = &mut this.machine.sync.init_onces[id];
6566assert_eq!(
67 init_once.status,
68 InitOnceStatus::Begun,
69"completing already complete or uninit init once"
70);
7172 init_once.status = InitOnceStatus::Complete;
7374// Each complete happens-before the end of the wait
75if let Some(data_race) = &this.machine.data_race {
76 data_race
77 .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
78 }
7980// Wake up everyone.
81 // need to take the queue to avoid having `this` be borrowed multiple times
82for waiter in std::mem::take(&mut init_once.waiters) {
83 this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
84 }
8586 interp_ok(())
87 }
8889#[inline]
90fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
91let this = self.eval_context_mut();
92let init_once = &mut this.machine.sync.init_onces[id];
93assert_eq!(
94 init_once.status,
95 InitOnceStatus::Begun,
96"failing already completed or uninit init once"
97);
98// This is again uninitialized.
99init_once.status = InitOnceStatus::Uninitialized;
100101// Each complete happens-before the end of the wait
102if let Some(data_race) = &this.machine.data_race {
103 data_race
104 .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
105 }
106107// Wake up one waiting thread, so they can go ahead and try to init this.
108if let Some(waiter) = init_once.waiters.pop_front() {
109 this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
110 }
111112 interp_ok(())
113 }
114115/// Synchronize with the previous completion of an InitOnce.
116 /// Must only be called after checking that it is complete.
117#[inline]
118fn init_once_observe_completed(&mut self, id: InitOnceId) {
119let this = self.eval_context_mut();
120121assert_eq!(
122 this.init_once_status(id),
123 InitOnceStatus::Complete,
124"observing the completion of incomplete init once"
125);
126127 this.acquire_clock(&this.machine.sync.init_onces[id].clock);
128 }
129}