Condition variables are used with a Mutex to efficiently wait for an arbitrary condition to occur. It does this by atomically unlocking the mutex, blocking the thread until notified, and finally re-locking the mutex. Condition can be statically initialized and is at most @sizeOf(u64)
large.
Example:
var m = Mutex{};
var c = Condition{};
var predicate = false;
fn consumer() void {
m.lock();
defer m.unlock();
while (!predicate) {
c.wait(&m);
}
}
fn producer() void {
{
m.lock();
defer m.unlock();
predicate = true;
}
c.signal();
}
const thread = try std.Thread.spawn(.{}, producer, .{});
consumer();
thread.join();
Note that condition variables can only reliably unblock threads that are sequenced before them using the same Mutex. This means that the following is allowed to deadlock:
thread-1: mutex.lock()
thread-1: condition.wait(&mutex)
thread-2: // mutex.lock() (without this, the following signal may not see the waiting thread-1)
thread-2: // mutex.unlock() (this is optional for correctness once locked above, as signal can be called while holding the mutex)
thread-2: condition.signal()
Functions
fn broadcast(self: *Condition) void
Unblocks all threads currently blocked in a call to
wait()
ortimedWait()
wi…Unblocks all threads currently blocked in a call to
wait()
ortimedWait()
with a given Mutex. The blocked threads must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking.broadcast()
can be called with or without the relevant Mutex being acquired and have no “effect” if there’s no observable blocked threads.fn signal(self: *Condition) void
Unblocks at least one thread blocked in a call to
wait()
ortimedWait()
with…Unblocks at least one thread blocked in a call to
wait()
ortimedWait()
with a given Mutex. The blocked thread must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking.signal()
can be called with or without the relevant Mutex being acquired and have no “effect” if there’s no observable blocked threads.fn timedWait(self: *Condition, mutex: *Mutex, timeout_ns: u64) error{Timeout}!void
Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mu…
Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. “Atomically” here refers to accesses done on the Condition after acquiring the Mutex.
The Mutex must be locked by the caller’s thread when this function is called. A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex.
A blocking call to
timedWait()
is unblocked from one of the following conditions:- a spurious (“at random”) wake occurs
- the caller was blocked for around
timeout_ns
nanoseconds, in whicherror.Timeout
is returned. - a future call to
signal()
orbroadcast()
which has acquired the Mutex and is sequenced after thistimedWait()
.
Given
timedWait()
can be interrupted spuriously, the blocking condition should be checked continuously irrespective of any notifications fromsignal()
orbroadcast()
.fn wait(self: *Condition, mutex: *Mutex) void
Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mu…
Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. “Atomically” here refers to accesses done on the Condition after acquiring the Mutex.
The Mutex must be locked by the caller’s thread when this function is called. A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex.
A blocking call to wait() is unblocked from one of the following conditions:
- a spurious (“at random”) wake up occurs
- a future call to
signal()
orbroadcast()
which has acquired the Mutex and is sequenced after thiswait()
.
Given wait() can be interrupted spuriously, the blocking condition should be checked continuously irrespective of any notifications from
signal()
orbroadcast()
.