149. OnceLock::wait — Block a Thread Until Another One Initializes the Value
You have one thread loading a config and a handful of workers that can’t start until it’s ready. OnceLock::wait blocks until the value lands — no Condvar, no Mutex, no spin loop.
OnceLock<T> is a write-once cell: any number of threads can race to set it, but only the first wins. The usual reader API is get, which returns None until something has been stored:
| |
That’s fine when the reader can keep working without the value. But what if the reader genuinely needs it now? Before Rust 1.86 you’d reach for a Mutex<Option<T>> plus a Condvar, or spin in a loop calling get — both more code and more bugs than the problem deserves.
OnceLock::wait parks the calling thread until the cell is initialized, then hands back &T:
| |
Every consumer gets back the same &String — OnceLock only ever holds one value, so the borrow is shared and lives as long as the cell does. No cloning, no Arc wrapping.
wait plays nicely with the existing init helpers. If you have a fallible initializer that some threads might run and others just want to await, mix get_or_init with wait:
| |
A few things worth knowing:
waitblocks forever if nobody ever callsset(orget_or_initsucceeds). It’s a synchronization primitive, not a timeout — pair it withthread::spawnfor a producer you actually control.- It’s
&self, so any number of threads canwaiton the same cell at once. OnceLock<T>requiresT: Send + Syncto be shared across threads, same asArc.
For lazy-init that runs on first read, LazyLock is still the right tool. But when initialization happens elsewhere and other threads need to pause until it’s done, wait turns a Condvar dance into one method call.