diff --git a/Cargo.toml b/Cargo.toml index aebf71459..7d8cc3933 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ documentation = "https://doc.rust-lang.org/time" description = """ Utilities for working with time-related functions in Rust. """ +build = "build.rs" [dependencies] libc = "0.2.1" @@ -21,6 +22,9 @@ redox_syscall = "0.1" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.0", features = ["std", "minwinbase", "minwindef", "ntdef", "profileapi", "sysinfoapi", "timezoneapi"] } +[build-dependencies] +rustc_version = "0.2" + [dev-dependencies] log = "0.4" winapi = { version = "0.3.0", features = ["std", "processthreadsapi", "winbase"] } diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..30ca72c1c --- /dev/null +++ b/build.rs @@ -0,0 +1,8 @@ +extern crate rustc_version; +use rustc_version::{version, Version}; + +fn main() { + if version().unwrap() >= Version::parse("1.31.0").unwrap() { + println!("cargo:rustc-cfg=feature=\"has_const_fn\""); + } +} diff --git a/src/sys.rs b/src/sys.rs index 5ab79b03a..95894236f 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -873,8 +873,52 @@ mod inner { pub fn get_time() -> (i64, i32) { unsafe { - let mut ft = mem::zeroed(); - GetSystemTimeAsFileTime(&mut ft); + let mut ft: FILETIME = mem::zeroed(); + + // Based on work from: https://github.com/rust-lang/rust/issues/67266 + // + // Code adapted from Rust itself: + // + // https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/compat.rs + // + // We need one of the available functions in Windows that lets us + // get the current time. We only want to load the symbol once, because + // loading symbols is costly, and we also want to be thread-safe. + + use std::sync::atomic::{AtomicUsize, Ordering}; + use winapi::um::libloaderapi; + + #[cfg(not(feature = "has_const_fn"))] + use std::sync::atomic::{ATOMIC_USIZE_INIT}; + #[cfg(not(feature = "has_const_fn"))] + static PTR: AtomicUsize = ATOMIC_USIZE_INIT; + #[cfg(feature = "has_const_fn")] + static PTR: AtomicUsize = AtomicUsize::new(0); + + let addr = match PTR.load(Ordering::SeqCst) { + 0 => { + // Check if GetSystemTimePreciseAsFileTime is exported from Windows' kernel32, + // and take its address if so. Otherwise, fallback to the older + // GetSystemTimeAsFileTime function. + let module = b"kernel32\0".as_ptr() as *const i8; + let symbol = b"GetSystemTimePreciseAsFileTime\0".as_ptr() as *const i8; + let addr = { + let handle = libloaderapi::GetModuleHandleA(module); + match libloaderapi::GetProcAddress(handle, symbol) as usize { + 0 => GetSystemTimeAsFileTime as usize, // fallback function + addr => addr, + } + }; + PTR.store(addr, Ordering::SeqCst); + addr + } + addr => addr, // We already looked up the symbol - just return the address + }; + + // Call the function at `addr`: + type PFNGSTAFT = unsafe extern "system" fn(LPFILETIME); + mem::transmute::(addr)(&mut ft as LPFILETIME); + (file_time_to_unix_seconds(&ft), file_time_to_nsec(&ft)) } }