make all objects used with atomic operations volatile
[musl] / src / thread / pthread_once.c
1 #include "pthread_impl.h"
2
3 static void undo(void *control)
4 {
5         /* Wake all waiters, since the waiter status is lost when
6          * resetting control to the initial state. */
7         if (a_swap(control, 0) == 3)
8                 __wake(control, -1, 1);
9 }
10
11 int __pthread_once_full(pthread_once_t *control, void (*init)(void))
12 {
13         /* Try to enter initializing state. Four possibilities:
14          *  0 - we're the first or the other cancelled; run init
15          *  1 - another thread is running init; wait
16          *  2 - another thread finished running init; just return
17          *  3 - another thread is running init, waiters present; wait */
18
19         for (;;) switch (a_cas(control, 0, 1)) {
20         case 0:
21                 pthread_cleanup_push(undo, control);
22                 init();
23                 pthread_cleanup_pop(0);
24
25                 if (a_swap(control, 2) == 3)
26                         __wake(control, -1, 1);
27                 return 0;
28         case 1:
29                 /* If this fails, so will __wait. */
30                 a_cas(control, 1, 3);
31         case 3:
32                 __wait(control, 0, 3, 1);
33                 continue;
34         case 2:
35                 return 0;
36         }
37 }
38
39 int __pthread_once(pthread_once_t *control, void (*init)(void))
40 {
41         /* Return immediately if init finished before, but ensure that
42          * effects of the init routine are visible to the caller. */
43         if (*(volatile int *)control == 2) {
44                 a_barrier();
45                 return 0;
46         }
47         return __pthread_once_full(control, init);
48 }
49
50 weak_alias(__pthread_once, pthread_once);