From cb57484dcaeb4881c73f8a1174d4d5661ca2bbe2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 3 Apr 2019 12:18:38 -0700 Subject: std: Avoid usage of `Once` in `Instant` This commit removes usage of `Once` from the internal implementation of time utilities on OSX and Windows. It turns out that we accidentally hit a deadlock today (#59020) via events that look like: * A thread invokes `park_timeout` * Internally, only on OSX, `park_timeout` calls `Instant::elapsed` * Inside of `Instant::elapsed` on OSX we enter a `Once` to initialize global timer data * Inside of `Once`, it attempts to `park` This means on the same stack frame, when there's contention, we're calling `park` from inside `park_timeout`, causing a deadlock! The solution implemented in this commit was to remove usage of `Once` and instead just do a small dance with atomics. There's no real need we need to guarantee that the global information is only learned once, only that it's only *stored* once. This implementation may have multiple threads invoke `mach_timebase_info`, but only one will store the global information which will amortize the cost for all other threads. A similar fix has been applied to windows to be uniform across our implementations, but looking at the code on Windows no deadlock was possible. This is purely just a consistency update for Windows and in theory a slightly leaner implementation. Closes #59020 --- src/test/run-pass/issue-59020.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/test/run-pass/issue-59020.rs (limited to 'src/test') 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"); +} -- cgit 1.4.1-3-g733a5