A wake signal from one or more producers (typically the audio thread) to a single consumer thread that sleeps until there is work.
More...
|
| | WakeSignal () noexcept |
| | ~WakeSignal () |
| | WakeSignal (const WakeSignal &)=delete |
| WakeSignal & | operator= (const WakeSignal &)=delete |
| void | notify () noexcept |
| | Producer side, realtime safe. Call after the work is published.
|
| template<typename Ready> |
| bool | wait_until (Ready &&ready, Clock::time_point deadline) |
| | Consumer side.
|
| template<typename Ready> |
| bool | wait_for (Ready &&ready, Clock::duration timeout) |
| | wait_until() with a deadline timeout from now.
|
| std::uint64_t | posts () const noexcept |
| | Diagnostics: kernel posts the producers made, and times the consumer woke from a park (a post or a timeout).
|
| std::uint64_t | wakes () const noexcept |
| bool | consumer_parked () const noexcept |
| | Diagnostics: whether the consumer is currently parked (or about to be).
|
A wake signal from one or more producers (typically the audio thread) to a single consumer thread that sleeps until there is work.
The producer publishes its work first (a ring write, a release store of a slot state) and then calls notify(). The consumer calls wait_until() or wait_for() with a predicate that reads the same published state.
Realtime contract:
- notify() never allocates, never locks and never blocks. It is a sequentially consistent fence and one atomic load; it reaches the kernel (a dispatch semaphore on Apple, std::counting_semaphore elsewhere) only while the consumer is parked, so a producer whose consumer is busy makes no system call at all.
- wait_until() / wait_for() block in the kernel; call them from the consumer thread only, never from the audio thread.
No lost wakeups: the consumer stores parked, fences, then re-checks its predicate before sleeping; the producer publishes, fences, then loads parked. The two sequentially consistent fences guarantee that either the consumer sees the work in its re-check or the producer sees the consumer parked and posts. An extra post (two notifies while parked) only costs the consumer one more predicate check.
One consumer at a time. Destroy the signal only after the consumer has stopped waiting (join the consumer thread first).
ring.push(block);
wake.notify();
while (running) {
wake.wait_for([&] { return !ring.empty() || !running; }, 100ms);
drain(ring);
}