#include "pthread_impl.h" #include #define pthread_cleanup_push_static(f, x) do { static struct __ptcb __cb; _pthread_cleanup_push(&__cb, f, x) static void undo(void *flg) { once_flag * flag = flg; a_store(&flag->__cntrl, 0); __wake_priv(&flag->__cntrl, 1); } void call_once(once_flag *flag, void (*func)(void)) { __THRD_ABI_MARK; /* Return immediately if init finished before */ if (flag->__cntrl == 2) return; /* Try to enter initializing state. Three possibilities: * 0 - we're the first or the other cancelled; run init * 1 - another thread is running init; wait * 2 - another thread finished running init; just return */ for (;;) switch (a_cas(&flag->__cntrl, 0, 1)) { case 0: _pthread_cleanup_push((void*)&flag->__cb, undo, flag); func(); _pthread_cleanup_pop((void*)&flag->__cb, 0); a_store(&flag->__cntrl, 2); if (flag->__waiters) __wake(&flag->__cntrl, -1, 0); return; case 1: __wait_priv(&flag->__cntrl, &flag->__waiters, 1); continue; case 2: return; } }