miri/concurrency/
init_once.rs

1use std::collections::VecDeque;
2
3use rustc_index::Idx;
4
5use super::thread::DynUnblockCallback;
6use super::vector_clock::VClock;
7use crate::*;
8
9super::sync::declare_id!(InitOnceId);
10
11#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
12/// The current status of a one time initialization.
13pub enum InitOnceStatus {
14    #[default]
15    Uninitialized,
16    Begun,
17    Complete,
18}
19
20/// The one time initialization state.
21#[derive(Default, Debug)]
22pub(super) struct InitOnce {
23    status: InitOnceStatus,
24    waiters: VecDeque<ThreadId>,
25    clock: VClock,
26}
27
28impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
29pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
30    #[inline]
31    fn init_once_status(&mut self, id: InitOnceId) -> InitOnceStatus {
32        let this = self.eval_context_ref();
33        this.machine.sync.init_onces[id].status
34    }
35
36    /// Put the thread into the queue waiting for the initialization.
37    #[inline]
38    fn init_once_enqueue_and_block(&mut self, id: InitOnceId, callback: DynUnblockCallback<'tcx>) {
39        let this = self.eval_context_mut();
40        let thread = this.active_thread();
41        let init_once = &mut this.machine.sync.init_onces[id];
42        assert_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    }
46
47    /// Begin initializing this InitOnce. Must only be called after checking that it is currently
48    /// uninitialized.
49    #[inline]
50    fn init_once_begin(&mut self, id: InitOnceId) {
51        let this = self.eval_context_mut();
52        let init_once = &mut this.machine.sync.init_onces[id];
53        assert_eq!(
54            init_once.status,
55            InitOnceStatus::Uninitialized,
56            "beginning already begun or complete init once"
57        );
58        init_once.status = InitOnceStatus::Begun;
59    }
60
61    #[inline]
62    fn init_once_complete(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
63        let this = self.eval_context_mut();
64        let init_once = &mut this.machine.sync.init_onces[id];
65
66        assert_eq!(
67            init_once.status,
68            InitOnceStatus::Begun,
69            "completing already complete or uninit init once"
70        );
71
72        init_once.status = InitOnceStatus::Complete;
73
74        // Each complete happens-before the end of the wait
75        if 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        }
79
80        // Wake up everyone.
81        // need to take the queue to avoid having `this` be borrowed multiple times
82        for waiter in std::mem::take(&mut init_once.waiters) {
83            this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
84        }
85
86        interp_ok(())
87    }
88
89    #[inline]
90    fn init_once_fail(&mut self, id: InitOnceId) -> InterpResult<'tcx> {
91        let this = self.eval_context_mut();
92        let init_once = &mut this.machine.sync.init_onces[id];
93        assert_eq!(
94            init_once.status,
95            InitOnceStatus::Begun,
96            "failing already completed or uninit init once"
97        );
98        // This is again uninitialized.
99        init_once.status = InitOnceStatus::Uninitialized;
100
101        // Each complete happens-before the end of the wait
102        if 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        }
106
107        // Wake up one waiting thread, so they can go ahead and try to init this.
108        if let Some(waiter) = init_once.waiters.pop_front() {
109            this.unblock_thread(waiter, BlockReason::InitOnce(id))?;
110        }
111
112        interp_ok(())
113    }
114
115    /// Synchronize with the previous completion of an InitOnce.
116    /// Must only be called after checking that it is complete.
117    #[inline]
118    fn init_once_observe_completed(&mut self, id: InitOnceId) {
119        let this = self.eval_context_mut();
120
121        assert_eq!(
122            this.init_once_status(id),
123            InitOnceStatus::Complete,
124            "observing the completion of incomplete init once"
125        );
126
127        this.acquire_clock(&this.machine.sync.init_onces[id].clock);
128    }
129}