Change:
I was trying to troubleshoot this issue: and eventually we figured out that on my machine parktimeout wasn't waking up. I wrote this simple test code which demonstrates the issue: Most of the time it will only print this: Every once in a while it will finish normally. This behavior happens on stable and nightly and I tried a handfull of stable versions all the way back to 1.12 and they all got stuck. Windows (both msvc and gnu appear to never get stuck (at least I couldn't reproduce the issue)). I was able to duplicate this an another mac as well (both running 10.14) If only a single thread is started (or the park happens on the main thread) then I can't reproduce. I'm on 10.14; uname:
This definitely seems broken. Here's dtruss output:
It's a deadlock on the ONCE here:
As a workaround, everything should work fine as long as you construct an at least once in your process before parking.
Doesn't seem to help :(
Oh right sorry, you actually have to subtract two Instants to force that Once to complete. If you instead do at the start of main, it prevents the deadlock.
Works perfectly now! Thanks!

Discussion:
#[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { use crate::fmt; <del> use crate::sync::Once; </del> <ins> use crate::mem; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; </ins> use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration;

Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) } <del> fn info() -> &'static libc::mach_timebase_info { </del> <ins> fn info() -> libc::mach_timebase_info { </ins> static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info { numer: 0, denom: 0, }; <del> static ONCE: Once = Once::new(); </del> <ins> static STATE: AtomicUsize = AtomicUsize::new(0); </ins> unsafe { <del> ONCE.call_once(|| { libc::mach_timebase_info(&mut INFO); }); &INFO </del> <ins> // 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; </ins> } } }

mod perf_counter { use super::{NANOS_PER_SEC}; <del> use crate::sync::Once; </del> <ins> use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; </ins> use crate::sys_common::mul_div_u64; use crate::sys::c; use crate::sys::cvt;

fn frequency() -> c::LARGE_INTEGER { static mut FREQUENCY: c::LARGE_INTEGER = 0; <del> static ONCE: Once = Once::new(); </del> <ins> static STATE: AtomicUsize = AtomicUsize::new(0); </ins> unsafe { <del> ONCE.call_once(|| { cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap(); }); FREQUENCY </del> <ins> // 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; </ins> } }

<ins> // 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"); } </ins>