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)]
12pub enum InitOnceStatus {
14 #[default]
15 Uninitialized,
16 Begun,
17 Complete,
18}
19
20#[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 #[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 #[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 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 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 init_once.status = InitOnceStatus::Uninitialized;
100
101 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 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 #[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}