1 #include <threads.h>
2 
3 #include <assert.h>
4 
5 #include "futex_impl.h"
6 
7 enum {
8     STATE_INIT = 0, // we're the first; run init
9     STATE_WAIT = 1, // another thread is running init; wait
10     STATE_DONE = 2, // another thread finished running init; just return
11     STATE_WAKE = 3, // another thread is running init, waiters present; wait
12 };
13 
14 static_assert(STATE_INIT == ONCE_FLAG_INIT, "");
15 
once_full(once_flag * control,void (* init)(void))16 static void once_full(once_flag* control, void (*init)(void)) {
17     for (;;)
18         switch (a_cas_shim(control, STATE_INIT, STATE_WAIT)) {
19         case STATE_INIT:
20             init();
21 
22             if (atomic_exchange(control, STATE_DONE) == STATE_WAKE)
23                 _zx_futex_wake(control, UINT32_MAX);
24             return;
25         case STATE_WAIT:
26             /* If this fails, so will __wait. */
27             a_cas_shim(control, STATE_WAIT, STATE_WAKE);
28         case STATE_WAKE:
29             __wait(control, NULL, STATE_WAKE);
30             continue;
31         case STATE_DONE:
32             return;
33         }
34 }
35 
call_once(once_flag * control,void (* init)(void))36 void call_once(once_flag* control, void (*init)(void)) {
37     /* Return immediately if init finished before, but ensure that
38      * effects of the init routine are visible to the caller. */
39     if (atomic_load(control) == STATE_DONE) {
40         atomic_thread_fence(memory_order_seq_cst);
41         return;
42     }
43     once_full(control, init);
44 }
45