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}