]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
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 | } |