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}