master
1/*
2 Copyright (c) 2011-2016 mingw-w64 project
3
4 Permission is hereby granted, free of charge, to any person obtaining a
5 copy of this software and associated documentation files (the "Software"),
6 to deal in the Software without restriction, including without limitation
7 the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 DEALINGS IN THE SOFTWARE.
21*/
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <assert.h>
28#include <malloc.h>
29#include <stdio.h>
30
31#define WIN32_LEAN_AND_MEAN
32#include <windows.h>
33
34/* public header files */
35#include "pthread.h"
36#include "semaphore.h"
37/* internal header files */
38#include "barrier.h"
39#include "misc.h"
40
41static pthread_spinlock_t barrier_global = PTHREAD_SPINLOCK_INITIALIZER;
42
43static WINPTHREADS_ATTRIBUTE((noinline)) int
44barrier_unref(volatile pthread_barrier_t *barrier, int res)
45{
46 pthread_spin_lock(&barrier_global);
47 assert((((barrier_t *)*barrier)->valid == LIFE_BARRIER) && (((barrier_t *)*barrier)->busy > 0));
48 ((barrier_t *)*barrier)->busy -= 1;
49 pthread_spin_unlock(&barrier_global);
50 return res;
51}
52
53static WINPTHREADS_ATTRIBUTE((noinline)) int barrier_ref(volatile pthread_barrier_t *barrier)
54{
55 int r = 0;
56 pthread_spin_lock(&barrier_global);
57
58 if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
59 else {
60 ((barrier_t *)*barrier)->busy += 1;
61 }
62
63 pthread_spin_unlock(&barrier_global);
64
65 return r;
66}
67
68static WINPTHREADS_ATTRIBUTE((noinline)) int
69barrier_ref_destroy(volatile pthread_barrier_t *barrier, pthread_barrier_t *bDestroy)
70{
71 int r = 0;
72
73 *bDestroy = NULL;
74 pthread_spin_lock(&barrier_global);
75
76 if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
77 else {
78 barrier_t *b_ = (barrier_t *)*barrier;
79 if (b_->busy) r = EBUSY;
80 else {
81 *bDestroy = *barrier;
82 *barrier = NULL;
83 }
84 }
85
86 pthread_spin_unlock(&barrier_global);
87 return r;
88}
89
90static WINPTHREADS_ATTRIBUTE((noinline)) void
91barrier_ref_set (volatile pthread_barrier_t *barrier, void *v)
92{
93 pthread_spin_lock(&barrier_global);
94 *barrier = v;
95 pthread_spin_unlock(&barrier_global);
96}
97
98int pthread_barrier_destroy(pthread_barrier_t *b_)
99{
100 pthread_barrier_t bDestroy;
101 barrier_t *b;
102 int r;
103
104 while ((r = barrier_ref_destroy(b_,&bDestroy)) == EBUSY)
105 Sleep(0);
106
107 if (r)
108 return r;
109
110 b = (barrier_t *)bDestroy;
111
112 pthread_mutex_lock(&b->m);
113
114 if (sem_destroy(&b->sems[0]) != 0)
115 {
116 /* Could this happen? */
117 *b_ = bDestroy;
118 pthread_mutex_unlock (&b->m);
119 return EBUSY;
120 }
121 if (sem_destroy(&b->sems[1]) != 0)
122 {
123 sem_init (&b->sems[0], b->share, 0);
124 *b_ = bDestroy;
125 pthread_mutex_unlock (&b->m);
126 return -1;
127 }
128 pthread_mutex_unlock(&b->m);
129 if(pthread_mutex_destroy(&b->m) != 0) {
130 sem_init (&b->sems[0], b->share, 0);
131 sem_init (&b->sems[1], b->share, 0);
132 *b_ = bDestroy;
133 return -1;
134 }
135 b->valid = DEAD_BARRIER;
136 free(bDestroy);
137 return 0;
138
139}
140
141int
142pthread_barrier_init (pthread_barrier_t *b_, const void *attr,
143 unsigned int count)
144{
145 barrier_t *b;
146
147 if (!count || !b_)
148 return EINVAL;
149
150 if ((b = (pthread_barrier_t)calloc(1,sizeof(*b))) == NULL)
151 return ENOMEM;
152 if (!attr || *((int **)attr) == NULL)
153 b->share = PTHREAD_PROCESS_PRIVATE;
154 else
155 memcpy (&b->share, *((void **) attr), sizeof (int));
156 b->total = count;
157 b->count = count;
158 b->valid = LIFE_BARRIER;
159 b->sel = 0;
160
161 if (pthread_mutex_init(&b->m, NULL) != 0)
162 {
163 free (b);
164 return ENOMEM;
165 }
166
167 if (sem_init(&b->sems[0], b->share, 0) != 0)
168 {
169 pthread_mutex_destroy(&b->m);
170 free (b);
171 return ENOMEM;
172 }
173 if (sem_init(&b->sems[1], b->share, 0) != 0)
174 {
175 pthread_mutex_destroy(&b->m);
176 sem_destroy(&b->sems[0]);
177 free (b);
178 return ENOMEM;
179 }
180 barrier_ref_set (b_,b);
181
182 return 0;
183}
184
185int pthread_barrier_wait(pthread_barrier_t *b_)
186{
187 long sel;
188 int r, e, rslt;
189 barrier_t *b;
190
191 r = barrier_ref(b_);
192 if(r) return r;
193
194 b = (barrier_t *)*b_;
195
196 if ((r = pthread_mutex_lock(&b->m)) != 0) return barrier_unref(b_,EINVAL);
197 sel = b->sel;
198 InterlockedDecrement((long*)&b->total);
199 if (b->total == 0)
200 {
201 b->total = b->count;
202 b->sel = (sel != 0 ? 0 : 1);
203 e = 1;
204 rslt = PTHREAD_BARRIER_SERIAL_THREAD;
205 r = (b->count > 1 ? sem_post_multiple (&b->sems[sel], b->count - 1) : 0);
206 }
207 else { e = 0; rslt= 0; }
208 pthread_mutex_unlock(&b->m);
209 if (!e)
210 r = sem_wait(&b->sems[sel]);
211
212 if (!r) r = rslt;
213 return barrier_unref(b_,r);
214}
215
216int pthread_barrierattr_init(void **attr)
217{
218 int *p;
219
220 if ((p = (int *) calloc (1, sizeof (int))) == NULL)
221 return ENOMEM;
222
223 *p = PTHREAD_PROCESS_PRIVATE;
224 *attr = p;
225
226 return 0;
227}
228
229int pthread_barrierattr_destroy(void **attr)
230{
231 void *p;
232 if (!attr || (p = *attr) == NULL)
233 return EINVAL;
234 *attr = NULL;
235 free (p);
236 return 0;
237}
238
239int pthread_barrierattr_setpshared(void **attr, int s)
240{
241 if (!attr || *attr == NULL
242 || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
243 return EINVAL;
244 memcpy (*attr, &s, sizeof (int));
245 return 0;
246}
247
248int pthread_barrierattr_getpshared(void **attr, int *s)
249{
250 if (!attr || !s || *attr == NULL)
251 return EINVAL;
252 memcpy (s, *attr, sizeof (int));
253 return 0;
254}