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