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 <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 = {.tv_sec = t->tv_sec, .tv_nsec = t->tv_nsec};
271 return __sem_timedwait (sem, &t64);
272}
273
274int
275sem_post (sem_t *sem)
276{
277 _sem_t *sv;
278
279 if (sem_std_enter (sem, &sv, 0) != 0)
280 return -1;
281
282 if (sv->value >= SEM_VALUE_MAX)
283 {
284 pthread_mutex_unlock (&sv->vlock);
285 return sem_result (ERANGE);
286 }
287 InterlockedIncrement (&sv->value);
288 if (sv->value > 0 || ReleaseSemaphore (sv->s, 1, NULL))
289 {
290 pthread_mutex_unlock (&sv->vlock);
291 return 0;
292 }
293 InterlockedDecrement (&sv->value);
294 pthread_mutex_unlock (&sv->vlock);
295
296 return sem_result (EINVAL);
297}
298
299int
300sem_post_multiple (sem_t *sem, int count)
301{
302 int waiters_count;
303 _sem_t *sv;
304
305 if (count <= 0)
306 return sem_result (EINVAL);
307 if (sem_std_enter (sem, &sv, 0) != 0)
308 return -1;
309
310 if (sv->value > (SEM_VALUE_MAX - count))
311 {
312 pthread_mutex_unlock (&sv->vlock);
313 return sem_result (ERANGE);
314 }
315 waiters_count = -sv->value;
316 sv->value += count;
317 /*InterlockedExchangeAdd((long*)&sv->value, (long) count);*/
318 if (waiters_count <= 0
319 || ReleaseSemaphore (sv->s,
320 (waiters_count < count ? waiters_count
321 : count), NULL))
322 {
323 pthread_mutex_unlock(&sv->vlock);
324 return 0;
325 }
326 /*InterlockedExchangeAdd((long*)&sv->value, -((long) count));*/
327 sv->value -= count;
328 pthread_mutex_unlock(&sv->vlock);
329 return sem_result (EINVAL);
330}
331
332sem_t *
333sem_open (const char *name, int oflag, mode_t mode, unsigned int value)
334{
335 sem_result (ENOSYS);
336 return NULL;
337}
338
339int
340sem_close (sem_t *sem)
341{
342 return sem_result (ENOSYS);
343}
344
345int
346sem_unlink (const char *name)
347{
348 return sem_result (ENOSYS);
349}
350
351int
352sem_getvalue (sem_t *sem, int *sval)
353{
354 _sem_t *sv;
355 int r;
356
357 if (!sval)
358 return sem_result (EINVAL);
359
360 if (!sem || (sv = *sem) == NULL)
361 return sem_result (EINVAL);
362
363 if ((r = pthread_mutex_lock (&sv->vlock)) != 0)
364 return sem_result (r);
365 if (*sem == NULL)
366 {
367 pthread_mutex_unlock (&sv->vlock);
368 return sem_result (EINVAL);
369 }
370
371 *sval = (int) sv->value;
372 pthread_mutex_unlock (&sv->vlock);
373 return 0;
374}