1 | #include "pthread_impl.h"
|
---|
2 |
|
---|
3 | static int pshared_barrier_wait(pthread_barrier_t *b)
|
---|
4 | {
|
---|
5 | int limit = (b->_b_limit & INT_MAX) + 1;
|
---|
6 | int ret = 0;
|
---|
7 | int v, w;
|
---|
8 |
|
---|
9 | if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;
|
---|
10 |
|
---|
11 | while ((v=a_cas(&b->_b_lock, 0, limit)))
|
---|
12 | __wait(&b->_b_lock, &b->_b_waiters, v, 0);
|
---|
13 |
|
---|
14 | /* Wait for <limit> threads to get to the barrier */
|
---|
15 | if (++b->_b_count == limit) {
|
---|
16 | a_store(&b->_b_count, 0);
|
---|
17 | ret = PTHREAD_BARRIER_SERIAL_THREAD;
|
---|
18 | if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
|
---|
19 | } else {
|
---|
20 | a_store(&b->_b_lock, 0);
|
---|
21 | if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
|
---|
22 | while ((v=b->_b_count)>0)
|
---|
23 | __wait(&b->_b_count, &b->_b_waiters2, v, 0);
|
---|
24 | }
|
---|
25 |
|
---|
26 | __vm_lock();
|
---|
27 |
|
---|
28 | /* Ensure all threads have a vm lock before proceeding */
|
---|
29 | if (a_fetch_add(&b->_b_count, -1)==1-limit) {
|
---|
30 | a_store(&b->_b_count, 0);
|
---|
31 | if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
|
---|
32 | } else {
|
---|
33 | while ((v=b->_b_count))
|
---|
34 | __wait(&b->_b_count, &b->_b_waiters2, v, 0);
|
---|
35 | }
|
---|
36 |
|
---|
37 | /* Perform a recursive unlock suitable for self-sync'd destruction */
|
---|
38 | do {
|
---|
39 | v = b->_b_lock;
|
---|
40 | w = b->_b_waiters;
|
---|
41 | } while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);
|
---|
42 |
|
---|
43 | /* Wake a thread waiting to reuse or destroy the barrier */
|
---|
44 | if (v==INT_MIN+1 || (v==1 && w))
|
---|
45 | __wake(&b->_b_lock, 1, 0);
|
---|
46 |
|
---|
47 | __vm_unlock();
|
---|
48 |
|
---|
49 | return ret;
|
---|
50 | }
|
---|
51 |
|
---|
52 | struct instance
|
---|
53 | {
|
---|
54 | volatile int count;
|
---|
55 | volatile int last;
|
---|
56 | volatile int waiters;
|
---|
57 | volatile int finished;
|
---|
58 | };
|
---|
59 |
|
---|
60 | int pthread_barrier_wait(pthread_barrier_t *b)
|
---|
61 | {
|
---|
62 | int limit = b->_b_limit;
|
---|
63 | struct instance *inst;
|
---|
64 |
|
---|
65 | /* Trivial case: count was set at 1 */
|
---|
66 | if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
|
---|
67 |
|
---|
68 | /* Process-shared barriers require a separate, inefficient wait */
|
---|
69 | if (limit < 0) return pshared_barrier_wait(b);
|
---|
70 |
|
---|
71 | /* Otherwise we need a lock on the barrier object */
|
---|
72 | while (a_swap(&b->_b_lock, 1))
|
---|
73 | __wait(&b->_b_lock, &b->_b_waiters, 1, 1);
|
---|
74 | inst = b->_b_inst;
|
---|
75 |
|
---|
76 | /* First thread to enter the barrier becomes the "instance owner" */
|
---|
77 | if (!inst) {
|
---|
78 | struct instance new_inst = { 0 };
|
---|
79 | int spins = 200;
|
---|
80 | b->_b_inst = inst = &new_inst;
|
---|
81 | a_store(&b->_b_lock, 0);
|
---|
82 | if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
|
---|
83 | while (spins-- && !inst->finished)
|
---|
84 | a_spin();
|
---|
85 | a_inc(&inst->finished);
|
---|
86 | while (inst->finished == 1)
|
---|
87 | __syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0) != -ENOSYS
|
---|
88 | || __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0);
|
---|
89 | return PTHREAD_BARRIER_SERIAL_THREAD;
|
---|
90 | }
|
---|
91 |
|
---|
92 | /* Last thread to enter the barrier wakes all non-instance-owners */
|
---|
93 | if (++inst->count == limit) {
|
---|
94 | b->_b_inst = 0;
|
---|
95 | a_store(&b->_b_lock, 0);
|
---|
96 | if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
|
---|
97 | a_store(&inst->last, 1);
|
---|
98 | if (inst->waiters)
|
---|
99 | __wake(&inst->last, -1, 1);
|
---|
100 | } else {
|
---|
101 | a_store(&b->_b_lock, 0);
|
---|
102 | if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
|
---|
103 | __wait(&inst->last, &inst->waiters, 0, 1);
|
---|
104 | }
|
---|
105 |
|
---|
106 | /* Last thread to exit the barrier wakes the instance owner */
|
---|
107 | if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
|
---|
108 | __wake(&inst->finished, 1, 1);
|
---|
109 |
|
---|
110 | return 0;
|
---|
111 | }
|
---|