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