Skip to content

Commit

Permalink
Auto merge of #116461 - ChrisDenton:sleep, r=thomcc
Browse files Browse the repository at this point in the history
Windows: Support sub-millisecond sleep

Use `CreateWaitableTimerExW` with `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`. Does not work before Windows 10, version 1803 so in that case we fallback to using `Sleep`.

I've created a `WaitableTimer` type so it can one day be adapted to also support waiting to an absolute time (which has been talked about). Note though that it currently returns `Err(())` because we can't do anything with the errors other than fallback to the old `Sleep`. Feel free to tell me to do errors properly. It just didn't seem worth constructing an `io::Error` if we're never going to surface it to the user. And it *should* all be infallible anyway unless the OS is too old to support it.

Closes #43376
  • Loading branch information
bors committed Oct 24, 2023
2 parents 271dcc1 + fbf2567 commit cee6db1
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 1 deletion.
6 changes: 6 additions & 0 deletions library/std/src/sys/windows/c/windows_sys.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
32 changes: 32 additions & 0 deletions library/std/src/sys/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) -> ();
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion library/std/src/sys/windows/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
38 changes: 38 additions & 0 deletions library/std/src/sys/windows/time.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<Self, ()> {
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) };
}
}
12 changes: 12 additions & 0 deletions src/tools/miri/src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" => {
Expand Down

0 comments on commit cee6db1

Please sign in to comment.