1 #include "cado.h" // IWYU pragma: keep
2 #include <stddef.h> // NULL
3 #include <errno.h>
4 #include <pthread.h>
5 #include "barrier.h"
6
barrier_init(barrier_t * barrier,int count)7 int barrier_init (barrier_t *barrier, int count)
8 {
9 int rc;
10
11 if (count == 0)
12 return -EINVAL;
13
14 rc = pthread_mutex_init (&barrier->lock, NULL);
15 if (rc != 0) return rc;
16
17 barrier->left = barrier->count = count;
18 barrier->event = 0;
19
20 rc = pthread_cond_init (&barrier->cv, NULL);
21 if (rc != 0) {
22 pthread_mutex_destroy (&barrier->lock);
23 return rc;
24 }
25
26 return 0;
27 }
28
barrier_destroy(barrier_t * barrier)29 int barrier_destroy (barrier_t *barrier)
30 {
31 int rc = EBUSY;
32
33 rc = pthread_mutex_lock (&barrier->lock);
34 if (rc != 0) return rc;
35
36 int ok = barrier->left == barrier->count;
37 rc = pthread_mutex_unlock (&barrier->lock);
38
39 if (!ok) return -EBUSY;
40 if (rc != 0) return rc;
41
42 int r = 0;
43
44 r = pthread_mutex_destroy (&barrier->lock);
45 rc = pthread_cond_destroy (&barrier->cv);
46 if (rc && !r) r = rc;
47
48 return r;
49 }
50
barrier_wait(barrier_t * barrier,void (* in)(int,void *),void (* out)(int,void *),void * arg)51 int barrier_wait(barrier_t * barrier,
52 void (*in)(int, void *),
53 void (*out)(int, void *), void * arg)
54 {
55 int rc;
56
57 rc = pthread_mutex_lock (&barrier->lock);
58 if (rc != 0) return rc;
59
60 /* It could be that not all threads have exited the previous barrier. As
61 * usual, only the contended case matters here. The uncontended case won't
62 * even see this loop.
63 */
64 for ( ; rc == 0 && (barrier->event & 1) ; ) {
65 rc = pthread_cond_wait (&barrier->cv, &barrier->lock);
66 }
67
68 --barrier->left;
69
70 /* Call the (*in) function with mutex locked, and sequential value,
71 * in order. */
72 if (in) (*in)(barrier->left, arg);
73
74 if (barrier->left) {
75 int event = barrier->event;
76
77 /* protect against possible spurious wakeups */
78 do {
79 rc = pthread_cond_wait (&barrier->cv, &barrier->lock);
80 /* Error codes are returned as negative numbers */
81 if (rc != 0) break;
82 } while (event == barrier->event);
83 } else {
84 ++barrier->event;
85
86 /* Wake up everybody. */
87 rc = pthread_cond_broadcast (&barrier->cv);
88
89 if (rc == 0)
90 rc = BARRIER_SERIAL_THREAD;
91 }
92
93 /* This has the mutex locked */
94 if (out) (*out)(barrier->left, arg);
95 ++barrier->left;
96
97 if (barrier->left == barrier->count) {
98 /* We're leaving last. Increase barrier->event, so that its low bit
99 * can be used as an indicator of previous barrier completion. */
100 barrier->event++;
101 pthread_cond_broadcast (&barrier->cv);
102 }
103 pthread_mutex_unlock (&barrier->lock);
104
105 /* error: negative number, -(error code)
106 * waker: return BARRIER_SERIAL_THREAD
107 * other: return 0 */
108 return rc;
109 }
110