fix missing barrier in pthread_once/call_once shortcut path
[musl] / src / thread / pthread_once.c
1 #include "pthread_impl.h"
2
3 static void undo(void *control)
4 {
5         a_store(control, 0);
6         __wake(control, 1, 1);
7 }
8
9 int __pthread_once(pthread_once_t *control, void (*init)(void))
10 {
11         static int waiters;
12
13         /* Return immediately if init finished before, but ensure that
14          * effects of the init routine are visible to the caller. */
15         if (*control == 2) {
16                 a_barrier();
17                 return 0;
18         }
19
20         /* Try to enter initializing state. Three possibilities:
21          *  0 - we're the first or the other cancelled; run init
22          *  1 - another thread is running init; wait
23          *  2 - another thread finished running init; just return */
24
25         for (;;) switch (a_cas(control, 0, 1)) {
26         case 0:
27                 pthread_cleanup_push(undo, control);
28                 init();
29                 pthread_cleanup_pop(0);
30
31                 a_store(control, 2);
32                 if (waiters) __wake(control, -1, 1);
33                 return 0;
34         case 1:
35                 __wait(control, &waiters, 1, 1);
36                 continue;
37         case 2:
38                 return 0;
39         }
40 }
41
42 weak_alias(__pthread_once, pthread_once);