1*2c2f96dcSApple OSS Distributions# Atomic Condition Variables for Thread Synchronization 2*2c2f96dcSApple OSS Distributions 3*2c2f96dcSApple OSS DistributionsQuickly synchronizing when multiple threads could send wakeups. 4*2c2f96dcSApple OSS Distributions 5*2c2f96dcSApple OSS Distributions## Overview 6*2c2f96dcSApple OSS Distributions 7*2c2f96dcSApple OSS Distributions`sched_cond_*` (see `sched_prim.h`) provides a means of optimized wake/sleep 8*2c2f96dcSApple OSS Distributionssynchronization on kernel threads. Specifically, it provides a wrapper for 9*2c2f96dcSApple OSS Distributions`assert_wait`/`thread_block` & `thread_wake` patterns with fast paths. 10*2c2f96dcSApple OSS Distributions 11*2c2f96dcSApple OSS Distributions## Interfaces 12*2c2f96dcSApple OSS Distributions* `sched_cond_t` / `sched_cond_atomic_t` - Atomic condition variable type to synchronize on 13*2c2f96dcSApple OSS Distributions* `sched_cond_init(sched_cond_t *cond)` - Initialize the atomic condition var 14*2c2f96dcSApple OSS Distributions* `sched_cond_wait(sched_cond_t *cond, ...)` - Set state to inactive and wait for a wakeup on cond 15*2c2f96dcSApple OSS Distributions* `sched_cond_signal(sched_cond_t *cond, ...)` - Issue a wakeup on cond for the specified thread 16*2c2f96dcSApple OSS Distributions* `sched_cond_ack(sched_cond_t *cond)` - Acknowledge the wakeup on cond and set state to active 17*2c2f96dcSApple OSS Distributions 18*2c2f96dcSApple OSS Distributions## Limitations of Existing Interfaces 19*2c2f96dcSApple OSS Distributions 20*2c2f96dcSApple OSS DistributionsConsider the following example of a producer-consumer relationship. 21*2c2f96dcSApple OSS Distributions 22*2c2f96dcSApple OSS Distributions### Producer Thread 23*2c2f96dcSApple OSS Distributions```c 24*2c2f96dcSApple OSS Distributionswhile(1) { 25*2c2f96dcSApple OSS Distributions ... 26*2c2f96dcSApple OSS Distributions thread_wake_thread(..., consumer_thread); // (A) 27*2c2f96dcSApple OSS Distributions} 28*2c2f96dcSApple OSS Distributions``` 29*2c2f96dcSApple OSS Distributions### Consumer Thread 30*2c2f96dcSApple OSS Distributions```c 31*2c2f96dcSApple OSS Distributionsvoid work_loop_continuation() 32*2c2f96dcSApple OSS Distributions{ 33*2c2f96dcSApple OSS Distributions // (B) 34*2c2f96dcSApple OSS Distributions ... 35*2c2f96dcSApple OSS Distributions assert_wait(...); // (C) 36*2c2f96dcSApple OSS Distributions thread_block(..., work_loop_continuation); // (D) 37*2c2f96dcSApple OSS Distributions} 38*2c2f96dcSApple OSS Distributions``` 39*2c2f96dcSApple OSS Distributions 40*2c2f96dcSApple OSS DistributionsThis scheme has two key inefficiences: 41*2c2f96dcSApple OSS Distributions1. Multiple calls to wake the consumer thread (A) may be made before the consumer_thread has awoken. 42*2c2f96dcSApple OSS Distributions This results in precious CPU cycles being spent in (A) to wake the thread despite the fact that 43*2c2f96dcSApple OSS Distributions it has already been queued. 44*2c2f96dcSApple OSS Distributions2. If in the time since waking (B) and blocking (D), the consumer thread has been sent a wakeup (A), 45*2c2f96dcSApple OSS Distributions the thread will still yield (D), thus spending precious CPU cycles setting itself up to block only 46*2c2f96dcSApple OSS Distributions to be immediately queued once more. 47*2c2f96dcSApple OSS Distributions 48*2c2f96dcSApple OSS Distributions 49*2c2f96dcSApple OSS Distributions## Example Usage 50*2c2f96dcSApple OSS Distributions 51*2c2f96dcSApple OSS Distributions`sched_cond_t` and its functions provide fast paths for (1) and (2) by wrapping `thread_wake_thread` and 52*2c2f96dcSApple OSS Distributions`assert_wait/thread_block` with atomic bit operations. 53*2c2f96dcSApple OSS Distributions 54*2c2f96dcSApple OSS DistributionsUsing these enhancements, the previous example can be revised to: 55*2c2f96dcSApple OSS Distributions 56*2c2f96dcSApple OSS Distributions### Producer Thread 57*2c2f96dcSApple OSS Distributions```c 58*2c2f96dcSApple OSS Distributionswhile(1) { 59*2c2f96dcSApple OSS Distributions ... 60*2c2f96dcSApple OSS Distributions sched_cond_signal(&my_cond, ..., consumer_thread); // (E) 61*2c2f96dcSApple OSS Distributions} 62*2c2f96dcSApple OSS Distributions``` 63*2c2f96dcSApple OSS Distributions### Consumer Thread 64*2c2f96dcSApple OSS Distributions```c 65*2c2f96dcSApple OSS Distributionsvoid work_loop_continuation() 66*2c2f96dcSApple OSS Distributions{ 67*2c2f96dcSApple OSS Distributions sched_cond_ack(&my_cond); // (F) 68*2c2f96dcSApple OSS Distributions while (1) { 69*2c2f96dcSApple OSS Distributions ... 70*2c2f96dcSApple OSS Distributions sched_cond_wait(&my_cond, ..., work_loop_continuation); // (G) 71*2c2f96dcSApple OSS Distributions } 72*2c2f96dcSApple OSS Distributions} 73*2c2f96dcSApple OSS Distributions``` 74*2c2f96dcSApple OSS Distributions 75*2c2f96dcSApple OSS DistributionsIn this example, the producer thread signals the consumer (E), resulting in an explicit wake (A) iff the consumer is 76*2c2f96dcSApple OSS Distributionsnot awake and has not already been issued an un-acked wakeup. Conversely, the consumer acks the wakeup (F) once awake, 77*2c2f96dcSApple OSS Distributionssignalling that it is active and clearing the queued wakeup. Once done with its consumption it attempts to wait on the 78*2c2f96dcSApple OSS Distributionscond (G), signalling that it is inactive and checking for any wakeups that have been issued since the last ack (F). 79*2c2f96dcSApple OSS DistributionsIf a wakeup has been issued, the consumer immediately acks the wakeup and returns to re-enter the work loop. Else, 80*2c2f96dcSApple OSS Distributionsit will block as in (D). 81*2c2f96dcSApple OSS Distributions 82*2c2f96dcSApple OSS Distributions### On acknowledging wakeups 83*2c2f96dcSApple OSS Distributions 84*2c2f96dcSApple OSS DistributionsOne may note that the adoption of `sched_cond_*` involves adding an additional step (ack) to the consumers work loop. This 85*2c2f96dcSApple OSS Distributionsstep is critical for two reasons. 86*2c2f96dcSApple OSS Distributions 87*2c2f96dcSApple OSS Distributions1. Wakeups can be coalesced without potential loss of data. By ack-ing the wakeup *prior* to doing work, wakeups 88*2c2f96dcSApple OSS Distributions that are issued while the thread is active are guaranteed to be observed because the consumer will check for wakeups since the 89*2c2f96dcSApple OSS Distributions last ack before giong to sleep. 90*2c2f96dcSApple OSS Distributions2. Wakeups need not explicitly `thread_wake` the consumer thread if it is already awake. This is because the consumer thread will not 91*2c2f96dcSApple OSS Distributions block if it observes a wakeup has been issued while it was awake. 92*2c2f96dcSApple OSS Distributions 93