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