1+ use std:: time:: Duration ;
2+
3+ use rustc_target:: abi:: Size ;
4+
15use crate :: concurrency:: init_once:: InitOnceStatus ;
26use crate :: concurrency:: thread:: MachineCallback ;
37use crate :: * ;
@@ -6,7 +10,6 @@ const SRWLOCK_ID_OFFSET: u64 = 0;
610const INIT_ONCE_ID_OFFSET : u64 = 0 ;
711
812impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
9-
1013#[ allow( non_snake_case) ]
1114pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
1215 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> {
221224
222225 this. eval_windows ( "c" , "TRUE" )
223226 }
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+ }
224330}
0 commit comments