diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index dad4a4223b2a3..78b1988afebd9 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -2505,9 +2505,12 @@ Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM Windows.Win32.System.Threading.CREATE_SUSPENDED Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET Windows.Win32.System.Threading.CreateEventW Windows.Win32.System.Threading.CreateProcessW Windows.Win32.System.Threading.CreateThread +Windows.Win32.System.Threading.CreateWaitableTimerExW Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS Windows.Win32.System.Threading.DEBUG_PROCESS Windows.Win32.System.Threading.DeleteProcThreadAttributeList @@ -2544,6 +2547,7 @@ Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS Windows.Win32.System.Threading.ReleaseSRWLockExclusive Windows.Win32.System.Threading.ReleaseSRWLockShared Windows.Win32.System.Threading.SetThreadStackGuarantee +Windows.Win32.System.Threading.SetWaitableTimer Windows.Win32.System.Threading.Sleep Windows.Win32.System.Threading.SleepConditionVariableSRW Windows.Win32.System.Threading.SleepEx @@ -2570,6 +2574,8 @@ Windows.Win32.System.Threading.TerminateProcess Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED Windows.Win32.System.Threading.THREAD_CREATION_FLAGS +Windows.Win32.System.Threading.TIMER_ALL_ACCESS +Windows.Win32.System.Threading.TIMER_MODIFY_STATE Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES Windows.Win32.System.Threading.TlsAlloc Windows.Win32.System.Threading.TlsFree diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index 20b44966a39f5..9ecd45e09ffab 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -151,6 +151,15 @@ extern "system" { ) -> HANDLE; } #[link(name = "kernel32")] +extern "system" { + pub fn CreateWaitableTimerExW( + lptimerattributes: *const SECURITY_ATTRIBUTES, + lptimername: PCWSTR, + dwflags: u32, + dwdesiredaccess: u32, + ) -> HANDLE; +} +#[link(name = "kernel32")] extern "system" { pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; } @@ -508,6 +517,17 @@ extern "system" { pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; } #[link(name = "kernel32")] +extern "system" { + pub fn SetWaitableTimer( + htimer: HANDLE, + lpduetime: *const i64, + lperiod: i32, + pfncompletionroutine: PTIMERAPCROUTINE, + lpargtocompletionroutine: *const ::core::ffi::c_void, + fresume: BOOL, + ) -> BOOL; +} +#[link(name = "kernel32")] extern "system" { pub fn Sleep(dwmilliseconds: u32) -> (); } @@ -1165,6 +1185,8 @@ pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32; pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32; pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32; pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32; +pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32; +pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32; pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32; pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32; pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32; @@ -3775,6 +3797,13 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; pub const PROGRESS_CONTINUE: u32 = 0u32; pub type PSTR = *mut u8; +pub type PTIMERAPCROUTINE = ::core::option::Option< + unsafe extern "system" fn( + lpargtocompletionroutine: *const ::core::ffi::c_void, + dwtimerlowvalue: u32, + dwtimerhighvalue: u32, + ) -> (), +>; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; @@ -3922,6 +3951,7 @@ pub type SYMBOLIC_LINK_FLAGS = u32; pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32; pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32; pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; +pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; #[repr(C)] pub struct SYSTEM_INFO { @@ -3968,6 +3998,8 @@ pub const TCP_NODELAY: i32 = 1i32; pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; pub type THREAD_CREATION_FLAGS = u32; +pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; +pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; #[repr(C)] pub struct TIMEVAL { pub tv_sec: i32, diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index 4b825d2a9f51f..1fe744935193c 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -12,6 +12,7 @@ use crate::time::Duration; use core::ffi::c_void; +use super::time::WaitableTimer; use super::to_u16s; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -87,7 +88,17 @@ impl Thread { } pub fn sleep(dur: Duration) { - unsafe { c::Sleep(super::dur2timeout(dur)) } + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behaviour of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + unsafe { c::Sleep(super::dur2timeout(dur)) } + } } pub fn handle(&self) -> &Handle { diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index b8209a8544585..bece48e799f8c 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -1,11 +1,13 @@ use crate::cmp::Ordering; use crate::fmt; use crate::mem; +use crate::ptr::{null, null_mut}; use crate::sys::c; use crate::sys_common::IntoInner; use crate::time::Duration; use core::hash::{Hash, Hasher}; +use core::ops::Neg; const NANOS_PER_SEC: u64 = 1_000_000_000; const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; @@ -222,3 +224,39 @@ mod perf_counter { qpc_value } } + +/// A timer you can wait on. +pub(super) struct WaitableTimer { + handle: c::HANDLE, +} +impl WaitableTimer { + /// Create a high-resolution timer. Will fail before Windows 10, version 1803. + pub fn high_resolution() -> Result { + let handle = unsafe { + c::CreateWaitableTimerExW( + null(), + null(), + c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, + c::TIMER_ALL_ACCESS, + ) + }; + if handle != null_mut() { Ok(Self { handle }) } else { Err(()) } + } + pub fn set(&self, duration: Duration) -> Result<(), ()> { + // Convert the Duration to a format similar to FILETIME. + // Negative values are relative times whereas positive values are absolute. + // Therefore we negate the relative duration. + let time = checked_dur2intervals(&duration).ok_or(())?.neg(); + let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) }; + if result != 0 { Ok(()) } else { Err(()) } + } + pub fn wait(&self) -> Result<(), ()> { + let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) }; + if result != c::WAIT_FAILED { Ok(()) } else { Err(()) } + } +} +impl Drop for WaitableTimer { + fn drop(&mut self) { + unsafe { c::CloseHandle(self.handle) }; + } +} diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 759a412c16a25..c887e18e97e8d 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -272,6 +272,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.Sleep(timeout)?; } + "CreateWaitableTimerExW" => { + let [attributes, name, flags, access] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + this.read_pointer(attributes)?; + this.read_pointer(name)?; + this.read_scalar(flags)?.to_u32()?; + this.read_scalar(access)?.to_u32()?; + // Unimplemented. Always return failure. + let not_supported = this.eval_windows("c", "ERROR_NOT_SUPPORTED"); + this.set_last_error(not_supported)?; + this.write_null(dest)?; + } // Synchronization primitives "AcquireSRWLockExclusive" => {