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