Skip to content

Commit fca182b

Browse files
committed
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`.
1 parent cae0791 commit fca182b

File tree

4 files changed

+88
-1
lines changed

4 files changed

+88
-1
lines changed

library/std/src/sys/windows/c/windows_sys.lst

+6
Original file line numberDiff line numberDiff line change
@@ -2503,9 +2503,12 @@ Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM
25032503
Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM
25042504
Windows.Win32.System.Threading.CREATE_SUSPENDED
25052505
Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT
2506+
Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
2507+
Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET
25062508
Windows.Win32.System.Threading.CreateEventW
25072509
Windows.Win32.System.Threading.CreateProcessW
25082510
Windows.Win32.System.Threading.CreateThread
2511+
Windows.Win32.System.Threading.CreateWaitableTimerExW
25092512
Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS
25102513
Windows.Win32.System.Threading.DEBUG_PROCESS
25112514
Windows.Win32.System.Threading.DeleteProcThreadAttributeList
@@ -2542,6 +2545,7 @@ Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS
25422545
Windows.Win32.System.Threading.ReleaseSRWLockExclusive
25432546
Windows.Win32.System.Threading.ReleaseSRWLockShared
25442547
Windows.Win32.System.Threading.SetThreadStackGuarantee
2548+
Windows.Win32.System.Threading.SetWaitableTimer
25452549
Windows.Win32.System.Threading.Sleep
25462550
Windows.Win32.System.Threading.SleepConditionVariableSRW
25472551
Windows.Win32.System.Threading.SleepEx
@@ -2568,6 +2572,8 @@ Windows.Win32.System.Threading.TerminateProcess
25682572
Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY
25692573
Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED
25702574
Windows.Win32.System.Threading.THREAD_CREATION_FLAGS
2575+
Windows.Win32.System.Threading.TIMER_ALL_ACCESS
2576+
Windows.Win32.System.Threading.TIMER_MODIFY_STATE
25712577
Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES
25722578
Windows.Win32.System.Threading.TlsAlloc
25732579
Windows.Win32.System.Threading.TlsFree

library/std/src/sys/windows/c/windows_sys.rs

+32
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ extern "system" {
151151
) -> HANDLE;
152152
}
153153
#[link(name = "kernel32")]
154+
extern "system" {
155+
pub fn CreateWaitableTimerExW(
156+
lptimerattributes: *const SECURITY_ATTRIBUTES,
157+
lptimername: PCWSTR,
158+
dwflags: u32,
159+
dwdesiredaccess: u32,
160+
) -> HANDLE;
161+
}
162+
#[link(name = "kernel32")]
154163
extern "system" {
155164
pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL;
156165
}
@@ -508,6 +517,17 @@ extern "system" {
508517
pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL;
509518
}
510519
#[link(name = "kernel32")]
520+
extern "system" {
521+
pub fn SetWaitableTimer(
522+
htimer: HANDLE,
523+
lpduetime: *const i64,
524+
lperiod: i32,
525+
pfncompletionroutine: PTIMERAPCROUTINE,
526+
lpargtocompletionroutine: *const ::core::ffi::c_void,
527+
fresume: BOOL,
528+
) -> BOOL;
529+
}
530+
#[link(name = "kernel32")]
511531
extern "system" {
512532
pub fn Sleep(dwmilliseconds: u32) -> ();
513533
}
@@ -1164,6 +1184,8 @@ pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32;
11641184
pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32;
11651185
pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32;
11661186
pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32;
1187+
pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32;
1188+
pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32;
11671189
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32;
11681190
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32;
11691191
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32;
@@ -3774,6 +3796,13 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32;
37743796
pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32;
37753797
pub const PROGRESS_CONTINUE: u32 = 0u32;
37763798
pub type PSTR = *mut u8;
3799+
pub type PTIMERAPCROUTINE = ::core::option::Option<
3800+
unsafe extern "system" fn(
3801+
lpargtocompletionroutine: *const ::core::ffi::c_void,
3802+
dwtimerlowvalue: u32,
3803+
dwtimerhighvalue: u32,
3804+
) -> (),
3805+
>;
37773806
pub type PWSTR = *mut u16;
37783807
pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32;
37793808
pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32;
@@ -3910,6 +3939,7 @@ pub type SYMBOLIC_LINK_FLAGS = u32;
39103939
pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32;
39113940
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32;
39123941
pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32;
3942+
pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32;
39133943
pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32;
39143944
#[repr(C)]
39153945
pub struct SYSTEM_INFO {
@@ -3956,6 +3986,8 @@ pub const TCP_NODELAY: i32 = 1i32;
39563986
pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32;
39573987
pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32;
39583988
pub type THREAD_CREATION_FLAGS = u32;
3989+
pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32;
3990+
pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32;
39593991
#[repr(C)]
39603992
pub struct TIMEVAL {
39613993
pub tv_sec: i32,

library/std/src/sys/windows/thread.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::time::Duration;
1212

1313
use libc::c_void;
1414

15+
use super::time::WaitableTimer;
1516
use super::to_u16s;
1617

1718
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -87,7 +88,17 @@ impl Thread {
8788
}
8889

8990
pub fn sleep(dur: Duration) {
90-
unsafe { c::Sleep(super::dur2timeout(dur)) }
91+
fn high_precision_sleep(dur: Duration) -> Result<(), ()> {
92+
let timer = WaitableTimer::high_resolution()?;
93+
timer.set(dur)?;
94+
timer.wait()
95+
}
96+
// Attempt to use high-precision sleep (Windows 10, version 1803+).
97+
// On error fallback to the standard `Sleep` function.
98+
// Also preserves the zero duration behaviour of `Sleep`.
99+
if dur.is_zero() || high_precision_sleep(dur).is_err() {
100+
unsafe { c::Sleep(super::dur2timeout(dur)) }
101+
}
91102
}
92103

93104
pub fn handle(&self) -> &Handle {

library/std/src/sys/windows/time.rs

+38
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::cmp::Ordering;
22
use crate::fmt;
33
use crate::mem;
4+
use crate::ptr::{null, null_mut};
45
use crate::sys::c;
56
use crate::sys_common::IntoInner;
67
use crate::time::Duration;
78

89
use core::hash::{Hash, Hasher};
10+
use core::ops::Neg;
911

1012
const NANOS_PER_SEC: u64 = 1_000_000_000;
1113
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
@@ -222,3 +224,39 @@ mod perf_counter {
222224
qpc_value
223225
}
224226
}
227+
228+
/// A timer you can wait on.
229+
pub(super) struct WaitableTimer {
230+
handle: c::HANDLE,
231+
}
232+
impl WaitableTimer {
233+
/// Create a high-resolution timer. Will fail before Windows 10, version 1803.
234+
pub fn high_resolution() -> Result<Self, ()> {
235+
let handle = unsafe {
236+
c::CreateWaitableTimerExW(
237+
null(),
238+
null(),
239+
c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
240+
c::TIMER_ALL_ACCESS,
241+
)
242+
};
243+
if handle != null_mut() { Ok(Self { handle }) } else { Err(()) }
244+
}
245+
pub fn set(&self, duration: Duration) -> Result<(), ()> {
246+
// Convert the Duration to a format similar to FILETIME.
247+
// Negative values are relative times whereas positive values are absolute.
248+
// Therefore we negate the relative duration.
249+
let time = checked_dur2intervals(&duration).ok_or(())?.neg();
250+
let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) };
251+
if result != 0 { Ok(()) } else { Err(()) }
252+
}
253+
pub fn wait(&self) -> Result<(), ()> {
254+
let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) };
255+
if result != c::WAIT_FAILED { Ok(()) } else { Err(()) }
256+
}
257+
}
258+
impl Drop for WaitableTimer {
259+
fn drop(&mut self) {
260+
unsafe { c::CloseHandle(self.handle) };
261+
}
262+
}

0 commit comments

Comments
 (0)