miri/concurrency/
init_once.rs1use std::cell::RefCell;
2use std::collections::VecDeque;
3use std::rc::Rc;
4
5use super::thread::DynUnblockCallback;
6use super::vector_clock::VClock;
7use crate::*;
8
9#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
10pub enum InitOnceStatus {
12 #[default]
13 Uninitialized,
14 Begun,
15 Complete,
16}
17
18#[derive(Default, Debug)]
20pub(super) struct InitOnce {
21 status: InitOnceStatus,
22 waiters: VecDeque<ThreadId>,
23 clock: VClock,
24}
25
26impl InitOnce {
27 #[inline]
28 pub fn status(&self) -> InitOnceStatus {
29 self.status
30 }
31
32 #[inline]
35 pub fn begin(&mut self) {
36 assert_eq!(
37 self.status(),
38 InitOnceStatus::Uninitialized,
39 "beginning already begun or complete init once"
40 );
41 self.status = InitOnceStatus::Begun;
42 }
43}
44
45#[derive(Default, Clone, Debug)]
46pub struct InitOnceRef(Rc<RefCell<InitOnce>>);
47
48impl InitOnceRef {
49 pub fn new() -> Self {
50 Self(Default::default())
51 }
52
53 pub fn status(&self) -> InitOnceStatus {
54 self.0.borrow().status()
55 }
56
57 pub fn begin(&self) {
58 self.0.borrow_mut().begin();
59 }
60}
61
62impl VisitProvenance for InitOnceRef {
63 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
65}
66
67impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
68pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
69 #[inline]
71 fn init_once_enqueue_and_block(
72 &mut self,
73 init_once_ref: InitOnceRef,
74 callback: DynUnblockCallback<'tcx>,
75 ) {
76 let this = self.eval_context_mut();
77 let thread = this.active_thread();
78 let mut init_once = init_once_ref.0.borrow_mut();
79 assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once");
80
81 init_once.waiters.push_back(thread);
82 this.block_thread(BlockReason::InitOnce, None, callback);
83 }
84
85 #[inline]
86 fn init_once_complete(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> {
87 let this = self.eval_context_mut();
88
89 let mut init_once = init_once_ref.0.borrow_mut();
90 assert_eq!(
91 init_once.status,
92 InitOnceStatus::Begun,
93 "completing already complete or uninit init once"
94 );
95
96 init_once.status = InitOnceStatus::Complete;
97
98 if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
100 data_race
101 .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
102 }
103
104 let waiters = std::mem::take(&mut init_once.waiters);
107 drop(init_once);
108 for waiter in waiters {
109 this.unblock_thread(waiter, BlockReason::InitOnce)?;
110 }
111
112 interp_ok(())
113 }
114
115 #[inline]
116 fn init_once_fail(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> {
117 let this = self.eval_context_mut();
118 let mut init_once = init_once_ref.0.borrow_mut();
119 assert_eq!(
120 init_once.status,
121 InitOnceStatus::Begun,
122 "failing already completed or uninit init once"
123 );
124 init_once.status = InitOnceStatus::Uninitialized;
126
127 if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
129 data_race
130 .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
131 }
132
133 if let Some(waiter) = init_once.waiters.pop_front() {
135 drop(init_once);
136 this.unblock_thread(waiter, BlockReason::InitOnce)?;
137 }
138
139 interp_ok(())
140 }
141
142 #[inline]
145 fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) {
146 let this = self.eval_context_mut();
147 let init_once = init_once_ref.0.borrow();
148
149 assert_eq!(
150 init_once.status,
151 InitOnceStatus::Complete,
152 "observing the completion of incomplete init once"
153 );
154
155 this.acquire_clock(&init_once.clock);
156 }
157}