diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 127ae6aa104..e21c32cd91b 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -114,7 +114,8 @@ impl Hash for Timespec { #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { use crate::fmt; - use crate::sync::Once; + use crate::mem; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration; @@ -229,18 +230,30 @@ mod inner { Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) } - fn info() -> &'static libc::mach_timebase_info { + fn info() -> libc::mach_timebase_info { static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info { numer: 0, denom: 0, }; - static ONCE: Once = Once::new(); + static STATE: AtomicUsize = AtomicUsize::new(0); unsafe { - ONCE.call_once(|| { - libc::mach_timebase_info(&mut INFO); - }); - &INFO + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return INFO; + } + + // ... otherwise learn for ourselves ... + let mut info = mem::zeroed(); + libc::mach_timebase_info(&mut info); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + INFO = info; + STATE.store(2, SeqCst); + } + return info; } } } diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 4c9d2aee157..e0f0e3a1a45 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -173,7 +173,7 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::{NANOS_PER_SEC}; - use crate::sync::Once; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys_common::mul_div_u64; use crate::sys::c; use crate::sys::cvt; @@ -210,13 +210,25 @@ mod perf_counter { fn frequency() -> c::LARGE_INTEGER { static mut FREQUENCY: c::LARGE_INTEGER = 0; - static ONCE: Once = Once::new(); + static STATE: AtomicUsize = AtomicUsize::new(0); unsafe { - ONCE.call_once(|| { - cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap(); - }); - FREQUENCY + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return FREQUENCY; + } + + // ... otherwise learn for ourselves ... + let mut frequency = 0; + cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap(); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + FREQUENCY = frequency; + STATE.store(2, SeqCst); + } + return frequency; } } diff --git a/src/test/run-pass/issue-59020.rs b/src/test/run-pass/issue-59020.rs new file mode 100644 index 00000000000..a2b11764a2f --- /dev/null +++ b/src/test/run-pass/issue-59020.rs @@ -0,0 +1,27 @@ +// edition:2018 +// run-pass +// ignore-emscripten no threads support + +use std::thread; +use std::time::Duration; + +fn main() { + let t1 = thread::spawn(|| { + let sleep = Duration::new(0,100_000); + for _ in 0..100 { + println!("Parking1"); + thread::park_timeout(sleep); + } + }); + + let t2 = thread::spawn(|| { + let sleep = Duration::new(0,100_000); + for _ in 0..100 { + println!("Parking2"); + thread::park_timeout(sleep); + } + }); + + t1.join().expect("Couldn't join thread 1"); + t2.join().expect("Couldn't join thread 2"); +}