1use std::time::Duration;
2
3use rustc_abi::{FieldIdx, Size};
4
5use crate::concurrency::init_once::{EvalContextExt as _, InitOnceStatus};
6use crate::concurrency::sync::{AccessKind, FutexRef, SyncObj};
7use crate::*;
8
9#[derive(Clone)]
10struct WindowsInitOnce {
11 init_once: InitOnceRef,
12}
13
14impl SyncObj for WindowsInitOnce {
15 fn on_access<'tcx>(&self, access_kind: AccessKind) -> InterpResult<'tcx> {
16 if !self.init_once.queue_is_empty() {
17 throw_ub_format!(
18 "{access_kind} of `INIT_ONCE` is forbidden while the queue is non-empty"
19 );
20 }
21 interp_ok(())
22 }
23
24 fn delete_on_write(&self) -> bool {
25 true
26 }
27}
28
29struct WindowsFutex {
30 futex: FutexRef,
31}
32
33impl SyncObj for WindowsFutex {}
34
35impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
36trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
37 fn init_once_get_data<'a>(
41 &'a mut self,
42 init_once_ptr: &OpTy<'tcx>,
43 ) -> InterpResult<'tcx, &'a WindowsInitOnce>
44 where
45 'tcx: 'a,
46 {
47 let this = self.eval_context_mut();
48
49 let init_once =
50 this.deref_pointer_as(init_once_ptr, this.windows_ty_layout("INIT_ONCE"))?;
51 let init_offset = Size::ZERO;
52
53 this.get_immovable_sync_with_static_init(
54 &init_once,
55 init_offset,
56 0,
57 1,
58 |this| {
59 let ptr_field = this.project_field(&init_once, FieldIdx::from_u32(0))?;
60 let val = this.read_target_usize(&ptr_field)?;
61 if val == 0 {
62 interp_ok(WindowsInitOnce { init_once: InitOnceRef::new() })
63 } else {
64 throw_ub_format!("`INIT_ONCE` was not properly initialized at this location, or it got overwritten");
65 }
66 },
67 )
68 }
69
70 fn init_once_try_begin(
72 &mut self,
73 init_once_ref: &InitOnceRef,
74 pending_place: &MPlaceTy<'tcx>,
75 dest: &MPlaceTy<'tcx>,
76 ) -> InterpResult<'tcx, bool> {
77 let this = self.eval_context_mut();
78 interp_ok(match init_once_ref.status() {
79 InitOnceStatus::Uninitialized => {
80 init_once_ref.begin();
81 this.write_scalar(this.eval_windows("c", "TRUE"), pending_place)?;
82 this.write_scalar(this.eval_windows("c", "TRUE"), dest)?;
83 true
84 }
85 InitOnceStatus::Complete => {
86 this.init_once_observe_completed(init_once_ref)?;
87 this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?;
88 this.write_scalar(this.eval_windows("c", "TRUE"), dest)?;
89 true
90 }
91 InitOnceStatus::Begun => false,
92 })
93 }
94}
95
96impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
97#[allow(non_snake_case)]
98pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
99 fn InitOnceBeginInitialize(
100 &mut self,
101 init_once_op: &OpTy<'tcx>,
102 flags_op: &OpTy<'tcx>,
103 pending_op: &OpTy<'tcx>,
104 context_op: &OpTy<'tcx>,
105 dest: &MPlaceTy<'tcx>,
106 ) -> InterpResult<'tcx> {
107 let this = self.eval_context_mut();
108
109 let init_once = this.init_once_get_data(init_once_op)?.init_once.clone();
110 let flags = this.read_scalar(flags_op)?.to_u32()?;
111 let pending_place = this.deref_pointer_as(pending_op, this.machine.layouts.i32)?;
113 let context = this.read_pointer(context_op)?;
114
115 if flags != 0 {
116 throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`");
117 }
118
119 if !this.ptr_is_null(context)? {
120 throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
121 }
122
123 if this.init_once_try_begin(&init_once, &pending_place, dest)? {
124 return interp_ok(());
126 }
127
128 let dest = dest.clone();
130 this.init_once_enqueue_and_block(
131 init_once.clone(),
132 callback!(
133 @capture<'tcx> {
134 init_once: InitOnceRef,
135 pending_place: MPlaceTy<'tcx>,
136 dest: MPlaceTy<'tcx>,
137 }
138 |this, unblock: UnblockKind| {
139 assert_eq!(unblock, UnblockKind::Ready);
140 let ret = this.init_once_try_begin(&init_once, &pending_place, &dest)?;
141 assert!(ret, "we were woken up but init_once_try_begin still failed");
142 interp_ok(())
143 }
144 ),
145 );
146 interp_ok(())
147 }
148
149 fn InitOnceComplete(
150 &mut self,
151 init_once_op: &OpTy<'tcx>,
152 flags_op: &OpTy<'tcx>,
153 context_op: &OpTy<'tcx>,
154 ) -> InterpResult<'tcx, Scalar> {
155 let this = self.eval_context_mut();
156
157 let init_once = this.init_once_get_data(init_once_op)?.init_once.clone();
158 let flags = this.read_scalar(flags_op)?.to_u32()?;
159 let context = this.read_pointer(context_op)?;
160
161 let success = if flags == 0 {
162 true
163 } else if flags == this.eval_windows_u32("c", "INIT_ONCE_INIT_FAILED") {
164 false
165 } else {
166 throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`");
167 };
168
169 if !this.ptr_is_null(context)? {
170 throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
171 }
172
173 if init_once.status() != InitOnceStatus::Begun {
174 throw_ub_format!(
176 "calling InitOnceComplete on a one time initialization that has not begun or is already completed"
177 );
178 }
179
180 if success {
181 this.init_once_complete(&init_once)?;
182 } else {
183 this.init_once_fail(&init_once)?;
184 }
185
186 interp_ok(this.eval_windows("c", "TRUE"))
187 }
188
189 fn WaitOnAddress(
190 &mut self,
191 ptr_op: &OpTy<'tcx>,
192 compare_op: &OpTy<'tcx>,
193 size_op: &OpTy<'tcx>,
194 timeout_op: &OpTy<'tcx>,
195 dest: &MPlaceTy<'tcx>,
196 ) -> InterpResult<'tcx> {
197 let this = self.eval_context_mut();
198
199 let ptr = this.read_pointer(ptr_op)?;
200 let compare = this.read_pointer(compare_op)?;
201 let size = this.read_target_usize(size_op)?;
202 let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?;
203
204 if size > 8 || !size.is_power_of_two() {
205 let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER");
206 this.set_last_error(invalid_param)?;
207 this.write_scalar(Scalar::from_i32(0), dest)?;
208 return interp_ok(());
209 };
210 let size = Size::from_bytes(size);
211
212 let timeout = if timeout_ms == this.eval_windows_u32("c", "INFINITE") {
213 None
214 } else {
215 let duration = Duration::from_millis(timeout_ms.into());
216 Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration))
217 };
218
219 this.atomic_fence(AtomicFenceOrd::SeqCst)?;
221
222 let layout = this.machine.layouts.uint(size).unwrap();
223 let futex_val =
224 this.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Acquire)?;
225 let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
226
227 if futex_val == compare_val {
228 let futex_ref = this
232 .get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
233 .unwrap()
234 .futex
235 .clone();
236
237 let dest = dest.clone();
238 this.futex_wait(
239 futex_ref,
240 u32::MAX, timeout,
242 callback!(
243 @capture<'tcx> {
244 dest: MPlaceTy<'tcx>
245 }
246 |this, unblock: UnblockKind| {
247 match unblock {
248 UnblockKind::Ready => {
249 this.write_int(1, &dest)
250 }
251 UnblockKind::TimedOut => {
252 this.set_last_error(IoError::WindowsError("ERROR_TIMEOUT"))?;
253 this.write_int(0, &dest)
254 }
255 }
256 }
257 ),
258 );
259 }
260
261 this.write_scalar(Scalar::from_i32(1), dest)?;
262
263 interp_ok(())
264 }
265
266 fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
267 let this = self.eval_context_mut();
268
269 let ptr = this.read_pointer(ptr_op)?;
270
271 this.atomic_fence(AtomicFenceOrd::SeqCst)?;
273
274 let Some(futex_ref) =
275 this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
276 else {
277 return interp_ok(());
279 };
280 let futex_ref = futex_ref.futex.clone();
281
282 this.futex_wake(&futex_ref, u32::MAX, 1)?;
283
284 interp_ok(())
285 }
286 fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
287 let this = self.eval_context_mut();
288
289 let ptr = this.read_pointer(ptr_op)?;
290
291 this.atomic_fence(AtomicFenceOrd::SeqCst)?;
293
294 let Some(futex_ref) =
295 this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
296 else {
297 return interp_ok(());
299 };
300 let futex_ref = futex_ref.futex.clone();
301
302 this.futex_wake(&futex_ref, u32::MAX, usize::MAX)?;
303
304 interp_ok(())
305 }
306}