improve pshared barriers
[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 ret = 0;
25         int v, w;
26
27         if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;
28
29         while ((v=a_cas(&b->_b_lock, 0, limit)))
30                 __wait(&b->_b_lock, &b->_b_waiters, v, 0);
31
32         /* Wait for <limit> threads to get to the barrier */
33         if (++b->_b_count == limit) {
34                 a_store(&b->_b_count, 0);
35                 ret = PTHREAD_BARRIER_SERIAL_THREAD;
36                 if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
37         } else {
38                 a_store(&b->_b_lock, 0);
39                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
40                 while ((v=b->_b_count)>0)
41                         __wait(&b->_b_count, &b->_b_waiters2, v, 0);
42         }
43
44         __vm_lock(+1);
45
46         /* Ensure all threads have a vm lock before proceeding */
47         if (a_fetch_add(&b->_b_count, -1)==1-limit) {
48                 a_store(&b->_b_count, 0);
49                 if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
50         } else {
51                 while ((v=b->_b_count))
52                         __wait(&b->_b_count, &b->_b_waiters2, v, 0);
53         }
54         
55         /* Perform a recursive unlock suitable for self-sync'd destruction */
56         do {
57                 v = b->_b_lock;
58                 w = b->_b_waiters;
59         } while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);
60
61         /* Wake a thread waiting to reuse or destroy the barrier */
62         if (v==INT_MIN+1 || (v==1 && w))
63                 __wake(&b->_b_lock, 1, 0);
64
65         __vm_unlock();
66
67         return ret;
68 }
69
70 struct instance
71 {
72         int count;
73         int last;
74         int waiters;
75         int finished;
76 };
77
78 int pthread_barrier_wait(pthread_barrier_t *b)
79 {
80         int limit = b->_b_limit;
81         struct instance *inst;
82
83         /* Trivial case: count was set at 1 */
84         if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
85
86         /* Process-shared barriers require a separate, inefficient wait */
87         if (limit < 0) return pshared_barrier_wait(b);
88
89         /* Otherwise we need a lock on the barrier object */
90         while (a_swap(&b->_b_lock, 1))
91                 __wait(&b->_b_lock, &b->_b_waiters, 1, 1);
92         inst = b->_b_inst;
93
94         /* First thread to enter the barrier becomes the "instance owner" */
95         if (!inst) {
96                 struct instance new_inst = { 0 };
97                 int spins = 10000;
98                 b->_b_inst = inst = &new_inst;
99                 a_store(&b->_b_lock, 0);
100                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
101                 while (spins-- && !inst->finished)
102                         a_spin();
103                 a_inc(&inst->finished);
104                 while (inst->finished == 1)
105                         __syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0);
106                 return PTHREAD_BARRIER_SERIAL_THREAD;
107         }
108
109         /* Last thread to enter the barrier wakes all non-instance-owners */
110         if (++inst->count == limit) {
111                 b->_b_inst = 0;
112                 a_store(&b->_b_lock, 0);
113                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
114                 a_store(&inst->last, 1);
115                 if (inst->waiters)
116                         __wake(&inst->last, -1, 1);
117         } else {
118                 a_store(&b->_b_lock, 0);
119                 if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
120                 __wait(&inst->last, &inst->waiters, 0, 1);
121         }
122
123         /* Last thread to exit the barrier wakes the instance owner */
124         if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
125                 __wake(&inst->finished, 1, 1);
126
127         return 0;
128 }