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