fix crash in pthread_cond_wait mutex-locked check
[musl] / src / thread / pthread_barrier_wait.c
1 #include "pthread_impl.h"
2
3 static int vmlock[2];
4
5 void __vm_lock(int inc)
6 {
7         for (;;) {
8                 int v = vmlock[0];
9                 if (inc*v < 0) __wait(vmlock, vmlock+1, v, 1);
10                 else if (a_cas(vmlock, v, v+inc)==v) break;
11         }
12 }
13
14 void __vm_unlock(void)
15 {
16         if (vmlock[0]>0) a_dec(vmlock);
17         else a_inc(vmlock);
18         if (vmlock[1]) __wake(vmlock, 1, 1);
19 }
20
21 static int pshared_barrier_wait(pthread_barrier_t *b)
22 {
23         int limit = (b->_b_limit & INT_MAX) + 1;
24         int seq;
25         int ret = 0;
26
27         if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
28
29         while (a_swap(&b->_b_lock, 1))
30                 __wait(&b->_b_lock, &b->_b_waiters, 1, 0);
31
32         seq = b->_b_seq;
33
34         if (++b->_b_count == limit) {
35                 ret = PTHREAD_BARRIER_SERIAL_THREAD;
36                 b->_b_seq++;
37                 __wake(&b->_b_seq, -1, 0);
38         } else {
39                 a_store(&b->_b_lock, 0);
40                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
41                 __wait(&b->_b_seq, 0, seq, 0);
42         }
43
44         __vm_lock(+1);
45
46         if (a_fetch_add(&b->_b_count, -1)==1) {
47                 b->_b_seq++;
48                 __wake(&b->_b_seq, -1, 0);
49                 a_store(&b->_b_lock, 0);
50                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
51         } else {
52                 __wait(&b->_b_seq, 0, seq+1, 0);
53         }
54         
55         __vm_unlock();
56
57         return ret;
58 }
59
60 struct instance
61 {
62         int count;
63         int last;
64         int waiters;
65         int finished;
66 };
67
68 int pthread_barrier_wait(pthread_barrier_t *b)
69 {
70         int limit = b->_b_limit;
71         struct instance *inst;
72
73         /* Trivial case: count was set at 1 */
74         if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
75
76         /* Process-shared barriers require a separate, inefficient wait */
77         if (limit < 0) return pshared_barrier_wait(b);
78
79         /* Otherwise we need a lock on the barrier object */
80         while (a_swap(&b->_b_lock, 1))
81                 __wait(&b->_b_lock, &b->_b_waiters, 1, 1);
82         inst = b->_b_inst;
83
84         /* First thread to enter the barrier becomes the "instance owner" */
85         if (!inst) {
86                 struct instance new_inst = { 0 };
87                 int spins = 10000;
88                 b->_b_inst = inst = &new_inst;
89                 a_store(&b->_b_lock, 0);
90                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
91                 while (spins-- && !inst->finished)
92                         a_spin();
93                 a_inc(&inst->finished);
94                 while (inst->finished == 1)
95                         __syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0);
96                 return PTHREAD_BARRIER_SERIAL_THREAD;
97         }
98
99         /* Last thread to enter the barrier wakes all non-instance-owners */
100         if (++inst->count == limit) {
101                 b->_b_inst = 0;
102                 a_store(&b->_b_lock, 0);
103                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
104                 a_store(&inst->last, 1);
105                 if (inst->waiters)
106                         __wake(&inst->last, -1, 1);
107         } else {
108                 a_store(&b->_b_lock, 0);
109                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
110                 __wait(&inst->last, &inst->waiters, 0, 1);
111         }
112
113         /* Last thread to exit the barrier wakes the instance owner */
114         if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
115                 __wake(&inst->finished, 1, 1);
116
117         return 0;
118 }