xref: /libuv/src/thread-common.c (revision 964f79f7)
1 /* Copyright libuv project contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "uv-common.h"
24 
25 #include <stdlib.h>
26 #ifndef _WIN32
27 #include <pthread.h>
28 #endif
29 
30 #if defined(PTHREAD_BARRIER_SERIAL_THREAD)
31 STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
32 #endif
33 
34 /* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
35 #if defined(_AIX) || \
36     defined(__OpenBSD__) || \
37     !defined(PTHREAD_BARRIER_SERIAL_THREAD)
uv_barrier_init(uv_barrier_t * barrier,unsigned int count)38 int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
39   int rc;
40 #ifdef _WIN32
41   uv_barrier_t* b;
42   b = barrier;
43 
44   if (barrier == NULL || count == 0)
45     return UV_EINVAL;
46 #else
47   struct _uv_barrier* b;
48 
49   if (barrier == NULL || count == 0)
50     return UV_EINVAL;
51 
52   b = uv__malloc(sizeof(*b));
53   if (b == NULL)
54     return UV_ENOMEM;
55 #endif
56 
57   b->in = 0;
58   b->out = 0;
59   b->threshold = count;
60 
61   rc = uv_mutex_init(&b->mutex);
62   if (rc != 0)
63     goto error2;
64 
65   /* TODO(vjnash): remove these uv_cond_t casts in v2. */
66   rc = uv_cond_init((uv_cond_t*) &b->cond);
67   if (rc != 0)
68     goto error;
69 
70 #ifndef _WIN32
71   barrier->b = b;
72 #endif
73   return 0;
74 
75 error:
76   uv_mutex_destroy(&b->mutex);
77 error2:
78 #ifndef _WIN32
79   uv__free(b);
80 #endif
81   return rc;
82 }
83 
84 
uv_barrier_wait(uv_barrier_t * barrier)85 int uv_barrier_wait(uv_barrier_t* barrier) {
86   int last;
87 #ifdef _WIN32
88   uv_barrier_t* b;
89   b = barrier;
90 #else
91   struct _uv_barrier* b;
92 
93   if (barrier == NULL || barrier->b == NULL)
94     return UV_EINVAL;
95 
96   b = barrier->b;
97 #endif
98 
99   uv_mutex_lock(&b->mutex);
100 
101   while (b->out != 0)
102     uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
103 
104   if (++b->in == b->threshold) {
105     b->in = 0;
106     b->out = b->threshold;
107     uv_cond_broadcast((uv_cond_t*) &b->cond);
108   } else {
109     do
110       uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
111     while (b->in != 0);
112   }
113 
114   last = (--b->out == 0);
115   if (last)
116     uv_cond_broadcast((uv_cond_t*) &b->cond);
117 
118   uv_mutex_unlock(&b->mutex);
119   return last;
120 }
121 
122 
uv_barrier_destroy(uv_barrier_t * barrier)123 void uv_barrier_destroy(uv_barrier_t* barrier) {
124 #ifdef _WIN32
125   uv_barrier_t* b;
126   b = barrier;
127 #else
128   struct _uv_barrier* b;
129   b = barrier->b;
130 #endif
131 
132   uv_mutex_lock(&b->mutex);
133 
134   assert(b->in == 0);
135   while (b->out != 0)
136     uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex);
137 
138   if (b->in != 0)
139     abort();
140 
141   uv_mutex_unlock(&b->mutex);
142   uv_mutex_destroy(&b->mutex);
143   uv_cond_destroy((uv_cond_t*) &b->cond);
144 
145 #ifndef  _WIN32
146   uv__free(barrier->b);
147   barrier->b = NULL;
148 #endif
149 }
150 
151 #else
152 
uv_barrier_init(uv_barrier_t * barrier,unsigned int count)153 int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
154   return UV__ERR(pthread_barrier_init(barrier, NULL, count));
155 }
156 
157 
uv_barrier_wait(uv_barrier_t * barrier)158 int uv_barrier_wait(uv_barrier_t* barrier) {
159   int rc;
160 
161   rc = pthread_barrier_wait(barrier);
162   if (rc != 0)
163     if (rc != PTHREAD_BARRIER_SERIAL_THREAD)
164       abort();
165 
166   return rc == PTHREAD_BARRIER_SERIAL_THREAD;
167 }
168 
169 
uv_barrier_destroy(uv_barrier_t * barrier)170 void uv_barrier_destroy(uv_barrier_t* barrier) {
171   if (pthread_barrier_destroy(barrier))
172     abort();
173 }
174 
175 #endif
176