1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| | #include "pthread_impl.h"
#include <threads.h>
#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;
}
}
|