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/*
 24 * Posix Condition Variables for Microsoft Windows.
 25 * 22-9-2010 Partly based on the ACE framework implementation.
 26 */
 27
 28#ifdef HAVE_CONFIG_H
 29#include "config.h"
 30#endif
 31
 32#include <malloc.h>
 33#include <stdio.h>
 34#include <time.h>
 35
 36#define WIN32_LEAN_AND_MEAN
 37#include <windows.h>
 38
 39#define WINPTHREAD_COND_DECL WINPTHREAD_API
 40
 41/* public header files */
 42#include "pthread.h"
 43#include "pthread_time.h"
 44/* internal header files */
 45#include "cond.h"
 46#include "misc.h"
 47#include "thread.h"
 48
 49#include "pthread_compat.h"
 50
 51int __pthread_shallcancel (void);
 52
 53static int do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val);
 54static int do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val);
 55static void cleanup_wait(void *arg);
 56
 57typedef struct sCondWaitHelper {
 58    cond_t *c;
 59    pthread_mutex_t *external_mutex;
 60    int *r;
 61} sCondWaitHelper;
 62
 63int do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout);
 64
 65static pthread_spinlock_t cond_locked = PTHREAD_SPINLOCK_INITIALIZER;
 66
 67static int
 68cond_static_init (pthread_cond_t *c)
 69{
 70  int r = 0;
 71
 72  pthread_spin_lock (&cond_locked);
 73  if (c == NULL)
 74    r = EINVAL;
 75  else if (*c == PTHREAD_COND_INITIALIZER)
 76    r = pthread_cond_init (c, NULL);
 77  else
 78    /* We assume someone was faster ... */
 79    r = 0;
 80  pthread_spin_unlock (&cond_locked);
 81  return r;
 82}
 83
 84int
 85pthread_condattr_destroy (pthread_condattr_t *a)
 86{
 87  if (!a)
 88    return EINVAL;
 89   *a = 0;
 90   return 0;
 91}
 92
 93int
 94pthread_condattr_init (pthread_condattr_t *a)
 95{
 96  if (!a)
 97    return EINVAL;
 98  *a = 0;
 99  return 0;
100}
101
102int
103pthread_condattr_getpshared (const pthread_condattr_t *a, int *s)
104{
105  if (!a || !s)
106    return EINVAL;
107  *s = *a;
108  return 0;
109}
110
111int
112pthread_condattr_getclock (const pthread_condattr_t *a, clockid_t *clock_id)
113{
114  if (!a || !clock_id)
115    return EINVAL;
116  *clock_id = 0;
117  return 0;
118}
119
120int
121pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clock_id)
122{
123  if (!a || clock_id != 0)
124    return EINVAL;
125  return 0;
126}
127
128int
129pthread_condattr_setpshared (pthread_condattr_t *a, int s)
130{
131  if (!a || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
132    return EINVAL;
133  if (s == PTHREAD_PROCESS_SHARED)
134    {
135       *a = PTHREAD_PROCESS_PRIVATE;
136       return ENOSYS;
137    }
138  *a = s;
139  return 0;
140}
141
142int
143pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a)
144{
145  cond_t *_c;
146  int r = 0;
147
148  if (!c)
149    return EINVAL;
150  if (a && *a == PTHREAD_PROCESS_SHARED)
151    return ENOSYS;
152
153  if ((_c = calloc(1, sizeof(*_c))) == NULL)
154    return ENOMEM;
155
156  _c->valid  = DEAD_COND;
157  _c->busy = 0;
158  _c->waiters_count_ = 0;
159  _c->waiters_count_gone_ = 0;
160  _c->waiters_count_unblock_ = 0;
161
162  _c->sema_q = CreateSemaphore (NULL,       /* no security */
163      0,          /* initially 0 */
164      0x7fffffff, /* max count */
165      NULL);      /* unnamed  */
166  _c->sema_b =  CreateSemaphore (NULL,       /* no security */
167      0,          /* initially 0 */
168      0x7fffffff, /* max count */
169      NULL);
170  if (_c->sema_q == NULL || _c->sema_b == NULL) {
171      if (_c->sema_q != NULL)
172	CloseHandle (_c->sema_q);
173      if (_c->sema_b != NULL)
174	CloseHandle (_c->sema_b);
175      free (_c);
176      r = EAGAIN;
177  } else {
178      InitializeCriticalSection(&_c->waiters_count_lock_);
179      InitializeCriticalSection(&_c->waiters_b_lock_);
180      InitializeCriticalSection(&_c->waiters_q_lock_);
181      _c->value_q = 0;
182      _c->value_b = 1;
183  }
184  if (!r)
185    {
186      _c->valid = LIFE_COND;
187      *c = (pthread_cond_t)_c;
188    }
189  else
190    *c = (pthread_cond_t)NULL;
191  return r;
192}
193
194int
195pthread_cond_destroy (pthread_cond_t *c)
196{
197  cond_t *_c;
198  int r;
199  if (!c || !*c)
200    return EINVAL;
201  if (*c == PTHREAD_COND_INITIALIZER)
202    {
203      pthread_spin_lock (&cond_locked);
204      if (*c == PTHREAD_COND_INITIALIZER)
205      {
206	*c = (pthread_cond_t)NULL;
207	r = 0;
208      }
209      else
210	r = EBUSY;
211      pthread_spin_unlock (&cond_locked);
212      return r;
213    }
214  _c = (cond_t *) *c;
215  r = do_sema_b_wait(_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
216  if (r != 0)
217    return r;
218  if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
219    {
220       do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
221       return EBUSY;
222    }
223  if (_c->waiters_count_ > _c->waiters_count_gone_)
224    {
225      r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
226      if (!r) r = EBUSY;
227      LeaveCriticalSection(&_c->waiters_count_lock_);
228      return r;
229    }
230  *c = (pthread_cond_t)NULL;
231  do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
232
233  if (!CloseHandle (_c->sema_q) && !r)
234    r = EINVAL;
235  if (!CloseHandle (_c->sema_b) && !r)
236    r = EINVAL;
237  LeaveCriticalSection (&_c->waiters_count_lock_);
238  DeleteCriticalSection(&_c->waiters_count_lock_);
239  DeleteCriticalSection(&_c->waiters_b_lock_);
240  DeleteCriticalSection(&_c->waiters_q_lock_);
241  _c->valid  = DEAD_COND;
242  free(_c);
243  return 0;
244}
245
246int
247pthread_cond_signal (pthread_cond_t *c)
248{
249  cond_t *_c;
250  int r;
251
252  if (!c || !*c)
253    return EINVAL;
254  _c = (cond_t *)*c;
255  if (_c == (cond_t *)PTHREAD_COND_INITIALIZER)
256    return 0;
257  else if (_c->valid != (unsigned int)LIFE_COND)
258    return EINVAL;
259
260  EnterCriticalSection (&_c->waiters_count_lock_);
261  /* If there aren't any waiters, then this is a no-op.   */
262  if (_c->waiters_count_unblock_ != 0)
263    {
264      if (_c->waiters_count_ == 0)
265      {
266	LeaveCriticalSection (&_c->waiters_count_lock_);
267	/* pthread_testcancel(); */
268	return 0;
269      }
270      _c->waiters_count_ -= 1;
271      _c->waiters_count_unblock_ += 1;
272    }
273  else if (_c->waiters_count_ > _c->waiters_count_gone_)
274    {
275      r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
276      if (r != 0)
277      {
278	LeaveCriticalSection (&_c->waiters_count_lock_);
279	/* pthread_testcancel(); */
280	return r;
281      }
282      if (_c->waiters_count_gone_ != 0)
283      {
284	_c->waiters_count_ -= _c->waiters_count_gone_;
285	_c->waiters_count_gone_ = 0;
286      }
287      _c->waiters_count_ -= 1;
288      _c->waiters_count_unblock_ = 1;
289    }
290  else
291    {
292      LeaveCriticalSection (&_c->waiters_count_lock_);
293      /* pthread_testcancel(); */
294      return 0;
295    }
296  LeaveCriticalSection (&_c->waiters_count_lock_);
297  r = do_sema_b_release(_c->sema_q, 1,&_c->waiters_q_lock_,&_c->value_q);
298  /* pthread_testcancel(); */
299  return r;
300}
301
302int
303pthread_cond_broadcast (pthread_cond_t *c)
304{
305  cond_t *_c;
306  int r;
307  int relCnt = 0;
308
309  if (!c || !*c)
310    return EINVAL;
311  _c = (cond_t *)*c;
312  if (_c == (cond_t*)PTHREAD_COND_INITIALIZER)
313    return 0;
314  else if (_c->valid != (unsigned int)LIFE_COND)
315    return EINVAL;
316
317  EnterCriticalSection (&_c->waiters_count_lock_);
318  /* If there aren't any waiters, then this is a no-op.   */
319  if (_c->waiters_count_unblock_ != 0)
320    {
321      if (_c->waiters_count_ == 0)
322      {
323	LeaveCriticalSection (&_c->waiters_count_lock_);
324	/* pthread_testcancel(); */
325	return 0;
326      }
327      relCnt = _c->waiters_count_;
328      _c->waiters_count_ = 0;
329      _c->waiters_count_unblock_ += relCnt;
330    }
331  else if (_c->waiters_count_ > _c->waiters_count_gone_)
332    {
333      r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
334      if (r != 0)
335      {
336	LeaveCriticalSection (&_c->waiters_count_lock_);
337	/* pthread_testcancel(); */
338	return r;
339      }
340      if (_c->waiters_count_gone_ != 0)
341      {
342	_c->waiters_count_ -= _c->waiters_count_gone_;
343	_c->waiters_count_gone_ = 0;
344      }
345      relCnt = _c->waiters_count_;
346      _c->waiters_count_ = 0;
347      _c->waiters_count_unblock_ = relCnt;
348    }
349  else
350    {
351      LeaveCriticalSection (&_c->waiters_count_lock_);
352      /* pthread_testcancel(); */
353      return 0;
354    }
355  LeaveCriticalSection (&_c->waiters_count_lock_);
356  r = do_sema_b_release(_c->sema_q, relCnt,&_c->waiters_q_lock_,&_c->value_q);
357  /* pthread_testcancel(); */
358  return r;
359}
360
361int
362pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *external_mutex)
363{
364  sCondWaitHelper ch;
365  cond_t *_c;
366  int r;
367
368  /* pthread_testcancel(); */
369
370  if (!c || *c == (pthread_cond_t)NULL)
371    return EINVAL;
372  _c = (cond_t *)*c;
373  if (*c == PTHREAD_COND_INITIALIZER)
374  {
375    r = cond_static_init(c);
376    if (r != 0 && r != EBUSY)
377      return r;
378    _c = (cond_t *) *c;
379  } else if (_c->valid != (unsigned int)LIFE_COND)
380    return EINVAL;
381
382tryagain:
383  r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
384  if (r != 0)
385    return r;
386
387  if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
388  {
389    r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
390    if (r != 0)
391      return r;
392    sched_yield();
393    goto tryagain;
394  }
395
396  _c->waiters_count_++;
397  LeaveCriticalSection(&_c->waiters_count_lock_);
398  r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
399  if (r != 0)
400    return r;
401
402  ch.c = _c;
403  ch.r = &r;
404  ch.external_mutex = external_mutex;
405
406  pthread_cleanup_push(cleanup_wait, (void *) &ch);
407  r = pthread_mutex_unlock(external_mutex);
408  if (!r)
409    r = do_sema_b_wait (_c->sema_q, 0, INFINITE,&_c->waiters_q_lock_,&_c->value_q);
410
411  pthread_cleanup_pop(1);
412  return r;
413}
414
415static int
416pthread_cond_timedwait_impl (pthread_cond_t *c, pthread_mutex_t *external_mutex, const struct _timespec64 *t, int rel)
417{
418  sCondWaitHelper ch;
419  DWORD dwr;
420  int r;
421  cond_t *_c;
422
423  /* pthread_testcancel(); */
424
425  if (!c || !*c)
426    return EINVAL;
427  _c = (cond_t *)*c;
428  if (_c == (cond_t *)PTHREAD_COND_INITIALIZER)
429  {
430    r = cond_static_init(c);
431    if (r && r != EBUSY)
432      return r;
433    _c = (cond_t *) *c;
434  } else if ((_c)->valid != (unsigned int)LIFE_COND)
435    return EINVAL;
436
437  if (rel == 0)
438  {
439    dwr = dwMilliSecs(_pthread_rel_time_in_ms(t));
440  }
441  else
442  {
443    dwr = dwMilliSecs(_pthread_time_in_ms_from_timespec(t));
444  }
445
446tryagain:
447  r = do_sema_b_wait (_c->sema_b, 0, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
448  if (r != 0)
449    return r;
450
451  if (!TryEnterCriticalSection (&_c->waiters_count_lock_))
452  {
453    r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
454    if (r != 0)
455      return r;
456    sched_yield();
457    goto tryagain;
458  }
459
460  _c->waiters_count_++;
461  LeaveCriticalSection(&_c->waiters_count_lock_);
462  r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
463  if (r != 0)
464    return r;
465
466  ch.c = _c;
467  ch.r = &r;
468  ch.external_mutex = external_mutex;
469  {
470    pthread_cleanup_push(cleanup_wait, (void *) &ch);
471
472    r = pthread_mutex_unlock(external_mutex);
473    if (!r)
474      r = do_sema_b_wait (_c->sema_q, 0, dwr,&_c->waiters_q_lock_,&_c->value_q);
475
476    pthread_cleanup_pop(1);
477  }
478  return r;
479}
480
481int
482pthread_cond_timedwait64(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec64 *t)
483{
484  return pthread_cond_timedwait_impl(c, m, t, 0);
485}
486
487int
488pthread_cond_timedwait32(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec32 *t)
489{
490  struct _timespec64 t64 = {.tv_sec = t->tv_sec, .tv_nsec = t->tv_nsec};
491  return pthread_cond_timedwait_impl(c, m, &t64, 0);
492}
493
494int
495pthread_cond_timedwait64_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec64 *t)
496{
497  return pthread_cond_timedwait_impl(c, m, t, 1);
498}
499
500int
501pthread_cond_timedwait32_relative_np(pthread_cond_t *c, pthread_mutex_t *m, const struct _timespec32 *t)
502{
503  struct _timespec64 t64 = {.tv_sec = t->tv_sec, .tv_nsec = t->tv_nsec};
504  return pthread_cond_timedwait_impl(c, m, &t64, 1);
505}
506
507static void
508cleanup_wait (void *arg)
509{
510  int n, r;
511  sCondWaitHelper *ch = (sCondWaitHelper *) arg;
512  cond_t *_c;
513
514  _c = ch->c;
515  EnterCriticalSection (&_c->waiters_count_lock_);
516  n = _c->waiters_count_unblock_;
517  if (n != 0)
518    _c->waiters_count_unblock_ -= 1;
519  else if ((INT_MAX/2) - 1 == _c->waiters_count_gone_)
520  {
521    _c->waiters_count_gone_ += 1;
522    r = do_sema_b_wait (_c->sema_b, 1, INFINITE,&_c->waiters_b_lock_,&_c->value_b);
523    if (r != 0)
524    {
525      LeaveCriticalSection(&_c->waiters_count_lock_);
526      ch->r[0] = r;
527      return;
528    }
529    _c->waiters_count_ -= _c->waiters_count_gone_;
530    r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
531    if (r != 0)
532    {
533      LeaveCriticalSection(&_c->waiters_count_lock_);
534      ch->r[0] = r;
535      return;
536    }
537    _c->waiters_count_gone_ = 0;
538  }
539  else
540    _c->waiters_count_gone_ += 1;
541  LeaveCriticalSection (&_c->waiters_count_lock_);
542
543  if (n == 1)
544  {
545    r = do_sema_b_release (_c->sema_b, 1,&_c->waiters_b_lock_,&_c->value_b);
546    if (r != 0)
547    {
548      ch->r[0] = r;
549      return;
550    }
551  }
552  r = pthread_mutex_lock(ch->external_mutex);
553  if (r != 0)
554    ch->r[0] = r;
555}
556
557static int
558do_sema_b_wait (HANDLE sema, int nointerrupt, DWORD timeout,CRITICAL_SECTION *cs, LONG *val)
559{
560  int r;
561  LONG v;
562  EnterCriticalSection(cs);
563  InterlockedDecrement(val);
564  v = val[0];
565  LeaveCriticalSection(cs);
566  if (v >= 0)
567    return 0;
568  r = do_sema_b_wait_intern (sema, nointerrupt, timeout);
569  EnterCriticalSection(cs);
570  if (r != 0)
571    InterlockedIncrement(val);
572  LeaveCriticalSection(cs);
573  return r;
574}
575
576int
577do_sema_b_wait_intern (HANDLE sema, int nointerrupt, DWORD timeout)
578{
579  HANDLE arr[2];
580  DWORD maxH = 1;
581  int r = 0;
582  DWORD res, dt;
583  if (nointerrupt == 1)
584  {
585    res = _pthread_wait_for_single_object(sema, timeout);
586    switch (res) {
587    case WAIT_TIMEOUT:
588	r = ETIMEDOUT;
589	break;
590    case WAIT_ABANDONED:
591	r = EPERM;
592	break;
593    case WAIT_OBJECT_0:
594	break;
595    default:
596	/*We can only return EINVAL though it might not be posix compliant  */
597	r = EINVAL;
598    }
599    if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
600      r = 0;
601    return r;
602  }
603  arr[0] = sema;
604  arr[1] = (HANDLE) pthread_getevent ();
605  if (arr[1] != NULL) maxH += 1;
606  if (maxH == 2)
607  {
608redo:
609      res = _pthread_wait_for_multiple_objects(maxH, arr, 0, timeout);
610      switch (res) {
611      case WAIT_TIMEOUT:
612	  r = ETIMEDOUT;
613	  break;
614      case (WAIT_OBJECT_0 + 1):
615          ResetEvent(arr[1]);
616          if (nointerrupt != 2)
617	    {
618            pthread_testcancel();
619            return EINVAL;
620	    }
621	  pthread_testcancel ();
622	  goto redo;
623      case WAIT_ABANDONED:
624	  r = EPERM;
625	  break;
626      case WAIT_OBJECT_0:
627          r = 0;
628	  break;
629      default:
630	  /*We can only return EINVAL though it might not be posix compliant  */
631	  r = EINVAL;
632      }
633      if (r != 0 && r != EINVAL && WaitForSingleObject(arr[0], 0) == WAIT_OBJECT_0)
634	r = 0;
635      if (r != 0 && nointerrupt != 2 && __pthread_shallcancel ())
636	return EINVAL;
637      return r;
638  }
639  if (timeout == INFINITE)
640  {
641    do {
642      res = _pthread_wait_for_single_object(sema, 40);
643      switch (res) {
644      case WAIT_TIMEOUT:
645	  r = ETIMEDOUT;
646	  break;
647      case WAIT_ABANDONED:
648	  r = EPERM;
649	  break;
650      case WAIT_OBJECT_0:
651          r = 0;
652	  break;
653      default:
654	  /*We can only return EINVAL though it might not be posix compliant  */
655	  r = EINVAL;
656      }
657      if (r != 0 && __pthread_shallcancel ())
658      {
659	if (nointerrupt != 2)
660	  pthread_testcancel();
661	return EINVAL;
662      }
663    } while (r == ETIMEDOUT);
664    if (r != 0 && r != EINVAL && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
665      r = 0;
666    return r;
667  }
668  dt = 20;
669  do {
670    if (dt > timeout) dt = timeout;
671    res = _pthread_wait_for_single_object(sema, dt);
672    switch (res) {
673    case WAIT_TIMEOUT:
674	r = ETIMEDOUT;
675	break;
676    case WAIT_ABANDONED:
677	r = EPERM;
678	break;
679    case WAIT_OBJECT_0:
680	r = 0;
681	break;
682    default:
683	/*We can only return EINVAL though it might not be posix compliant  */
684	r = EINVAL;
685    }
686    timeout -= dt;
687    if (timeout != 0 && r != 0 && __pthread_shallcancel ())
688      return EINVAL;
689  } while (r == ETIMEDOUT && timeout != 0);
690  if (r != 0 && r == ETIMEDOUT && WaitForSingleObject(sema, 0) == WAIT_OBJECT_0)
691    r = 0;
692  if (r != 0 && nointerrupt != 2)
693    pthread_testcancel();
694  return r;
695}
696
697static int
698do_sema_b_release(HANDLE sema, LONG count,CRITICAL_SECTION *cs, LONG *val)
699{
700  int wc;
701  EnterCriticalSection(cs);
702  if (((long long) val[0] + (long long) count) > (long long) 0x7fffffffLL)
703  {
704    LeaveCriticalSection(cs);
705    return ERANGE;
706  }
707  wc = -val[0];
708  InterlockedExchangeAdd(val, count);
709  if (wc <= 0 || ReleaseSemaphore(sema, (wc < count ? wc : count), NULL))
710  {
711    LeaveCriticalSection(cs);
712    return 0;
713  }
714  InterlockedExchangeAdd(val, -count);
715  LeaveCriticalSection(cs);
716  return EINVAL;
717}