1
+ use std:: time:: Duration ;
2
+
3
+ use rustc_target:: abi:: Size ;
4
+
1
5
use crate :: concurrency:: init_once:: InitOnceStatus ;
2
6
use crate :: concurrency:: thread:: MachineCallback ;
3
7
use crate :: * ;
@@ -6,7 +10,6 @@ const SRWLOCK_ID_OFFSET: u64 = 0;
6
10
const INIT_ONCE_ID_OFFSET : u64 = 0 ;
7
11
8
12
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
9
-
10
13
#[ allow( non_snake_case) ]
11
14
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
12
15
fn AcquireSRWLockExclusive ( & mut self , lock_op : & OpTy < ' tcx , Provenance > ) -> InterpResult < ' tcx > {
@@ -221,4 +224,107 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
221
224
222
225
this. eval_windows ( "c" , "TRUE" )
223
226
}
227
+
228
+ fn WaitOnAddress (
229
+ & mut self ,
230
+ ptr_op : & OpTy < ' tcx , Provenance > ,
231
+ compare_op : & OpTy < ' tcx , Provenance > ,
232
+ size_op : & OpTy < ' tcx , Provenance > ,
233
+ timeout_op : & OpTy < ' tcx , Provenance > ,
234
+ dest : & PlaceTy < ' tcx , Provenance > ,
235
+ ) -> InterpResult < ' tcx > {
236
+ let this = self . eval_context_mut ( ) ;
237
+
238
+ let ptr = this. read_pointer ( ptr_op) ?;
239
+ let compare = this. read_pointer ( compare_op) ?;
240
+ let size = this. read_scalar ( size_op) ?. to_machine_usize ( this) ?;
241
+ let timeout_ms = this. read_scalar ( timeout_op) ?. to_u32 ( ) ?;
242
+
243
+ let thread = this. get_active_thread ( ) ;
244
+ let addr = ptr. addr ( ) . bytes ( ) ;
245
+
246
+ if size > 8 || !size. is_power_of_two ( ) {
247
+ let invalid_param = this. eval_windows ( "c" , "ERROR_INVALID_PARAMETER" ) ?;
248
+ this. set_last_error ( invalid_param) ?;
249
+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , dest) ?;
250
+ return Ok ( ( ) ) ;
251
+ } ;
252
+ let size = Size :: from_bytes ( size) ;
253
+
254
+ let timeout_time = if timeout_ms == this. eval_windows ( "c" , "INFINITE" ) ?. to_u32 ( ) ? {
255
+ None
256
+ } else {
257
+ this. check_no_isolation ( "`WaitOnAddress` with non-infinite timeout" ) ?;
258
+
259
+ let duration = Duration :: from_millis ( timeout_ms. into ( ) ) ;
260
+ Some ( Time :: Monotonic ( this. machine . clock . now ( ) . checked_add ( duration) . unwrap ( ) ) )
261
+ } ;
262
+
263
+ // See the Linux futex implementation for why this fence exists.
264
+ this. atomic_fence ( AtomicFenceOrd :: SeqCst ) ?;
265
+
266
+ let layout = this. machine . layouts . uint ( size) . unwrap ( ) ;
267
+ let futex_val = this
268
+ . read_scalar_atomic ( & MPlaceTy :: from_aligned_ptr ( ptr, layout) , AtomicReadOrd :: Relaxed ) ?;
269
+ let compare_val = this. read_scalar ( & MPlaceTy :: from_aligned_ptr ( compare, layout) . into ( ) ) ?;
270
+
271
+ if futex_val == compare_val {
272
+ // If the values are the same, we have to block.
273
+ this. block_thread ( thread) ;
274
+ this. futex_wait ( addr, thread, u32:: MAX ) ;
275
+
276
+ if let Some ( timeout_time) = timeout_time {
277
+ struct Callback < ' tcx > {
278
+ thread : ThreadId ,
279
+ addr : u64 ,
280
+ dest : PlaceTy < ' tcx , Provenance > ,
281
+ }
282
+
283
+ impl < ' tcx > VisitTags for Callback < ' tcx > {
284
+ fn visit_tags ( & self , visit : & mut dyn FnMut ( SbTag ) ) {
285
+ let Callback { thread : _, addr : _, dest } = self ;
286
+ dest. visit_tags ( visit) ;
287
+ }
288
+ }
289
+
290
+ impl < ' mir , ' tcx : ' mir > MachineCallback < ' mir , ' tcx > for Callback < ' tcx > {
291
+ fn call ( & self , this : & mut MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx > {
292
+ this. unblock_thread ( self . thread ) ;
293
+ this. futex_remove_waiter ( self . addr , self . thread ) ;
294
+ let error_timeout = this. eval_windows ( "c" , "ERROR_TIMEOUT" ) ?;
295
+ this. set_last_error ( error_timeout) ?;
296
+ this. write_scalar ( Scalar :: from_i32 ( 0 ) , & self . dest ) ?;
297
+
298
+ Ok ( ( ) )
299
+ }
300
+ }
301
+
302
+ this. register_timeout_callback (
303
+ thread,
304
+ timeout_time,
305
+ Box :: new ( Callback { thread, addr, dest : dest. clone ( ) } ) ,
306
+ ) ;
307
+ }
308
+ }
309
+
310
+ this. write_scalar ( Scalar :: from_i32 ( 1 ) , dest) ?;
311
+
312
+ Ok ( ( ) )
313
+ }
314
+
315
+ fn WakeByAddressSingle ( & mut self , ptr_op : & OpTy < ' tcx , Provenance > ) -> InterpResult < ' tcx > {
316
+ let this = self . eval_context_mut ( ) ;
317
+
318
+ let ptr = this. read_pointer ( ptr_op) ?;
319
+
320
+ // See the Linux futex implementation for why this fence exists.
321
+ this. atomic_fence ( AtomicFenceOrd :: SeqCst ) ?;
322
+
323
+ if let Some ( thread) = this. futex_wake ( ptr. addr ( ) . bytes ( ) , u32:: MAX ) {
324
+ this. unblock_thread ( thread) ;
325
+ this. unregister_timeout_callback_if_exists ( thread) ;
326
+ }
327
+
328
+ Ok ( ( ) )
329
+ }
224
330
}
0 commit comments