1#include <threads.h> 2 3#include <assert.h> 4 5#include "futex_impl.h" 6 7enum { 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 14static_assert(STATE_INIT == ONCE_FLAG_INIT, ""); 15 16static 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 __wake(control, -1); 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 36void 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