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 #include <windows.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include "pthread.h"
27 #include "thread.h"
28 #include "misc.h"
29 #include "semaphore.h"
30 #include "sem.h"
31 #include "ref.h"
32
33 int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout);
34
35 static int
sem_result(int res)36 sem_result (int res)
37 {
38 if (res != 0) {
39 errno = res;
40 return -1;
41 }
42 return 0;
43 }
44
45 int
sem_init(sem_t * sem,int pshared,unsigned int value)46 sem_init (sem_t *sem, int pshared, unsigned int value)
47 {
48 _sem_t *sv;
49
50 if (!sem || value > (unsigned int)SEM_VALUE_MAX)
51 return sem_result (EINVAL);
52 if (pshared != PTHREAD_PROCESS_PRIVATE)
53 return sem_result (EPERM);
54
55 if (!(sv = (sem_t) calloc (1,sizeof (*sv))))
56 return sem_result (ENOMEM);
57
58 sv->value = value;
59 if (pthread_mutex_init (&sv->vlock, NULL) != 0)
60 {
61 free (sv);
62 return sem_result (ENOSPC);
63 }
64 if ((sv->s = CreateSemaphore (NULL, 0, SEM_VALUE_MAX, NULL)) == NULL)
65 {
66 pthread_mutex_destroy (&sv->vlock);
67 free (sv);
68 return sem_result (ENOSPC);
69 }
70
71 sv->valid = LIFE_SEM;
72 *sem = sv;
73 return 0;
74 }
75
76 int
sem_destroy(sem_t * sem)77 sem_destroy (sem_t *sem)
78 {
79 int r;
80 _sem_t *sv = NULL;
81
82 if (!sem || (sv = *sem) == NULL)
83 return sem_result (EINVAL);
84 if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
85 return sem_result (r);
86
87 #if 0
88 /* We don't wait for destroying a semaphore ...
89 or? */
90 if (sv->value < 0)
91 {
92 pthread_mutex_unlock (&sv->vlock);
93 return sem_result (EBUSY);
94 }
95 #endif
96
97 if (!CloseHandle (sv->s))
98 {
99 pthread_mutex_unlock (&sv->vlock);
100 return sem_result (EINVAL);
101 }
102 *sem = NULL;
103 sv->value = SEM_VALUE_MAX;
104 pthread_mutex_unlock(&sv->vlock);
105 Sleep (0);
106 while (pthread_mutex_destroy (&sv->vlock) == EBUSY)
107 Sleep (0);
108 sv->valid = DEAD_SEM;
109 free (sv);
110 return 0;
111 }
112
113 static int
sem_std_enter(sem_t * sem,_sem_t ** svp,int do_test)114 sem_std_enter (sem_t *sem,_sem_t **svp, int do_test)
115 {
116 int r;
117 _sem_t *sv;
118
119 if (do_test)
120 pthread_testcancel ();
121 if (!sem)
122 return sem_result (EINVAL);
123 sv = *sem;
124 if (sv == NULL)
125 return sem_result (EINVAL);
126
127 if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
128 return sem_result (r);
129
130 if (*sem == NULL)
131 {
132 pthread_mutex_unlock(&sv->vlock);
133 return sem_result (EINVAL);
134 }
135 *svp = sv;
136 return 0;
137 }
138
139 int
sem_trywait(sem_t * sem)140 sem_trywait (sem_t *sem)
141 {
142 _sem_t *sv;
143
144 if (sem_std_enter (sem, &sv, 0) != 0)
145 return -1;
146 if (sv->value <= 0)
147 {
148 pthread_mutex_unlock (&sv->vlock);
149 return sem_result (EAGAIN);
150 }
151 sv->value--;
152 pthread_mutex_unlock (&sv->vlock);
153
154 return 0;
155 }
156
157 struct sSemTimedWait
158 {
159 sem_t *p;
160 int *ret;
161 };
162
163 static void
clean_wait_sem(void * s)164 clean_wait_sem (void *s)
165 {
166 struct sSemTimedWait *p = (struct sSemTimedWait *) s;
167 _sem_t *sv = NULL;
168
169 if (sem_std_enter (p->p, &sv, 0) != 0)
170 return;
171
172 if (WaitForSingleObject (sv->s, 0) != WAIT_OBJECT_0)
173 InterlockedIncrement (&sv->value);
174 else if (p->ret)
175 p->ret[0] = 0;
176 pthread_mutex_unlock (&sv->vlock);
177 }
178
179 int
sem_wait(sem_t * sem)180 sem_wait (sem_t *sem)
181 {
182 long cur_v;
183 int ret = 0;
184 _sem_t *sv;
185 HANDLE semh;
186 struct sSemTimedWait arg;
187
188 if (sem_std_enter (sem, &sv, 1) != 0)
189 return -1;
190
191 arg.ret = &ret;
192 arg.p = sem;
193 InterlockedDecrement (&sv->value);
194 cur_v = sv->value;
195 semh = sv->s;
196 pthread_mutex_unlock (&sv->vlock);
197
198 if (cur_v >= 0)
199 return 0;
200 else
201 {
202 pthread_cleanup_push (clean_wait_sem, (void *) &arg);
203 ret = do_sema_b_wait_intern (semh, 2, INFINITE);
204 pthread_cleanup_pop (ret);
205 if (ret == EINVAL)
206 return 0;
207 }
208
209 if (!ret)
210 return 0;
211
212 return sem_result (ret);
213 }
214
215 int
sem_timedwait(sem_t * sem,const struct timespec * t)216 sem_timedwait (sem_t *sem, const struct timespec *t)
217 {
218 int cur_v, ret = 0;
219 DWORD dwr;
220 HANDLE semh;
221 _sem_t *sv;
222 struct sSemTimedWait arg;
223
224 if (!t)
225 return sem_wait (sem);
226 dwr = dwMilliSecs(_pthread_rel_time_in_ms (t));
227
228 if (sem_std_enter (sem, &sv, 1) != 0)
229 return -1;
230
231 arg.ret = &ret;
232 arg.p = sem;
233 InterlockedDecrement (&sv->value);
234 cur_v = sv->value;
235 semh = sv->s;
236 pthread_mutex_unlock(&sv->vlock);
237
238 if (cur_v >= 0)
239 return 0;
240 else
241 {
242 pthread_cleanup_push (clean_wait_sem, (void *) &arg);
243 ret = do_sema_b_wait_intern (semh, 2, dwr);
244 pthread_cleanup_pop (ret);
245 if (ret == EINVAL)
246 return 0;
247 }
248
249 if (!ret)
250 return 0;
251 return sem_result (ret);
252 }
253
254 int
sem_post(sem_t * sem)255 sem_post (sem_t *sem)
256 {
257 _sem_t *sv;;
258
259 if (sem_std_enter (sem, &sv, 0) != 0)
260 return -1;
261
262 if (sv->value >= SEM_VALUE_MAX)
263 {
264 pthread_mutex_unlock (&sv->vlock);
265 return sem_result (ERANGE);
266 }
267 InterlockedIncrement (&sv->value);
268 if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL))
269 {
270 pthread_mutex_unlock (&sv->vlock);
271 return 0;
272 }
273 InterlockedDecrement (&sv->value);
274 pthread_mutex_unlock (&sv->vlock);
275
276 return sem_result (EINVAL);
277 }
278
279 int
sem_post_multiple(sem_t * sem,int count)280 sem_post_multiple (sem_t *sem, int count)
281 {
282 int waiters_count;
283 _sem_t *sv;;
284
285 if (count <= 0)
286 return sem_result (EINVAL);
287 if (sem_std_enter (sem, &sv, 0) != 0)
288 return -1;
289
290 if (sv->value > (SEM_VALUE_MAX - count))
291 {
292 pthread_mutex_unlock (&sv->vlock);
293 return sem_result (ERANGE);
294 }
295 waiters_count = -sv->value;
296 sv->value += count;
297 /*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/
298 if (waiters_count <= 0
299 || ReleaseSemaphore (sv->s,
300 (waiters_count < count ? waiters_count
301 : count), NULL))
302 {
303 pthread_mutex_unlock(&sv->vlock);
304 return 0;
305 }
306 /*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/
307 sv->value -= count;
308 pthread_mutex_unlock(&sv->vlock);
309 return sem_result (EINVAL);
310 }
311
312 sem_t *
sem_open(const char * name,int oflag,mode_t mode,unsigned int value)313 sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
314 {
315 sem_result (ENOSYS);
316 return NULL;
317 }
318
319 int
sem_close(sem_t * sem)320 sem_close (sem_t *sem)
321 {
322 return sem_result (ENOSYS);
323 }
324
325 int
sem_unlink(const char * name)326 sem_unlink (const char *name)
327 {
328 return sem_result (ENOSYS);
329 }
330
331 int
sem_getvalue(sem_t * sem,int * sval)332 sem_getvalue (sem_t *sem, int *sval)
333 {
334 _sem_t *sv;
335 int r;
336
337 if (!sval)
338 return sem_result (EINVAL);
339
340 if (!sem || (sv = *sem) == NULL)
341 return sem_result (EINVAL);
342
343 if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
344 return sem_result (r);
345 if (*sem == NULL)
346 {
347 pthread_mutex_unlock (&sv->vlock);
348 return sem_result (EINVAL);
349 }
350
351 *sval = (int) sv->value;
352 pthread_mutex_unlock (&sv->vlock);
353 return 0;
354 }
355