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