xref: /xnu-10063.121.3/doc/primitives/sched_cond.md (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
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