1 /* Locking in multithreaded situations.
2 Copyright (C) 2005-2019 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
23 #include "glthread/lock.h"
25 /* ========================================================================= */
29 /* -------------------------- gl_lock_t datatype -------------------------- */
31 /* ------------------------- gl_rwlock_t datatype ------------------------- */
33 # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
35 # ifdef PTHREAD_RWLOCK_INITIALIZER
37 # if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
38 /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
41 glthread_rwlock_init_for_glibc (pthread_rwlock_t
*lock
)
43 pthread_rwlockattr_t attributes
;
46 err
= pthread_rwlockattr_init (&attributes
);
49 /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
50 causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
52 http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
53 err
= pthread_rwlockattr_setkind_np (&attributes
,
54 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
);
56 err
= pthread_rwlock_init(lock
, &attributes
);
57 /* pthread_rwlockattr_destroy always returns 0. It cannot influence the
59 pthread_rwlockattr_destroy (&attributes
);
67 glthread_rwlock_init_multithreaded (gl_rwlock_t
*lock
)
71 err
= pthread_rwlock_init (&lock
->rwlock
, NULL
);
74 lock
->initialized
= 1;
79 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t
*lock
)
81 if (!lock
->initialized
)
85 err
= pthread_mutex_lock (&lock
->guard
);
88 if (!lock
->initialized
)
90 err
= glthread_rwlock_init_multithreaded (lock
);
93 pthread_mutex_unlock (&lock
->guard
);
97 err
= pthread_mutex_unlock (&lock
->guard
);
101 return pthread_rwlock_rdlock (&lock
->rwlock
);
105 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t
*lock
)
107 if (!lock
->initialized
)
111 err
= pthread_mutex_lock (&lock
->guard
);
114 if (!lock
->initialized
)
116 err
= glthread_rwlock_init_multithreaded (lock
);
119 pthread_mutex_unlock (&lock
->guard
);
123 err
= pthread_mutex_unlock (&lock
->guard
);
127 return pthread_rwlock_wrlock (&lock
->rwlock
);
131 glthread_rwlock_unlock_multithreaded (gl_rwlock_t
*lock
)
133 if (!lock
->initialized
)
135 return pthread_rwlock_unlock (&lock
->rwlock
);
139 glthread_rwlock_destroy_multithreaded (gl_rwlock_t
*lock
)
143 if (!lock
->initialized
)
145 err
= pthread_rwlock_destroy (&lock
->rwlock
);
148 lock
->initialized
= 0;
157 glthread_rwlock_init_multithreaded (gl_rwlock_t
*lock
)
161 err
= pthread_mutex_init (&lock
->lock
, NULL
);
164 err
= pthread_cond_init (&lock
->waiting_readers
, NULL
);
167 err
= pthread_cond_init (&lock
->waiting_writers
, NULL
);
170 lock
->waiting_writers_count
= 0;
176 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t
*lock
)
180 err
= pthread_mutex_lock (&lock
->lock
);
183 /* Test whether only readers are currently running, and whether the runcount
184 field will not overflow, and whether no writer is waiting. The latter
185 condition is because POSIX recommends that "write locks shall take
186 precedence over read locks", to avoid "writer starvation". */
187 while (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers_count
== 0))
189 /* This thread has to wait for a while. Enqueue it among the
191 err
= pthread_cond_wait (&lock
->waiting_readers
, &lock
->lock
);
194 pthread_mutex_unlock (&lock
->lock
);
199 return pthread_mutex_unlock (&lock
->lock
);
203 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t
*lock
)
207 err
= pthread_mutex_lock (&lock
->lock
);
210 /* Test whether no readers or writers are currently running. */
211 while (!(lock
->runcount
== 0))
213 /* This thread has to wait for a while. Enqueue it among the
215 lock
->waiting_writers_count
++;
216 err
= pthread_cond_wait (&lock
->waiting_writers
, &lock
->lock
);
219 lock
->waiting_writers_count
--;
220 pthread_mutex_unlock (&lock
->lock
);
223 lock
->waiting_writers_count
--;
225 lock
->runcount
--; /* runcount becomes -1 */
226 return pthread_mutex_unlock (&lock
->lock
);
230 glthread_rwlock_unlock_multithreaded (gl_rwlock_t
*lock
)
234 err
= pthread_mutex_lock (&lock
->lock
);
237 if (lock
->runcount
< 0)
239 /* Drop a writer lock. */
240 if (!(lock
->runcount
== -1))
242 pthread_mutex_unlock (&lock
->lock
);
249 /* Drop a reader lock. */
250 if (!(lock
->runcount
> 0))
252 pthread_mutex_unlock (&lock
->lock
);
257 if (lock
->runcount
== 0)
259 /* POSIX recommends that "write locks shall take precedence over read
260 locks", to avoid "writer starvation". */
261 if (lock
->waiting_writers_count
> 0)
263 /* Wake up one of the waiting writers. */
264 err
= pthread_cond_signal (&lock
->waiting_writers
);
267 pthread_mutex_unlock (&lock
->lock
);
273 /* Wake up all waiting readers. */
274 err
= pthread_cond_broadcast (&lock
->waiting_readers
);
277 pthread_mutex_unlock (&lock
->lock
);
282 return pthread_mutex_unlock (&lock
->lock
);
286 glthread_rwlock_destroy_multithreaded (gl_rwlock_t
*lock
)
290 err
= pthread_mutex_destroy (&lock
->lock
);
293 err
= pthread_cond_destroy (&lock
->waiting_readers
);
296 err
= pthread_cond_destroy (&lock
->waiting_writers
);
304 /* --------------------- gl_recursive_lock_t datatype --------------------- */
306 # if HAVE_PTHREAD_MUTEX_RECURSIVE
308 # if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
311 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
313 pthread_mutexattr_t attributes
;
316 err
= pthread_mutexattr_init (&attributes
);
319 err
= pthread_mutexattr_settype (&attributes
, PTHREAD_MUTEX_RECURSIVE
);
322 pthread_mutexattr_destroy (&attributes
);
325 err
= pthread_mutex_init (lock
, &attributes
);
328 pthread_mutexattr_destroy (&attributes
);
331 err
= pthread_mutexattr_destroy (&attributes
);
340 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
342 pthread_mutexattr_t attributes
;
345 err
= pthread_mutexattr_init (&attributes
);
348 err
= pthread_mutexattr_settype (&attributes
, PTHREAD_MUTEX_RECURSIVE
);
351 pthread_mutexattr_destroy (&attributes
);
354 err
= pthread_mutex_init (&lock
->recmutex
, &attributes
);
357 pthread_mutexattr_destroy (&attributes
);
360 err
= pthread_mutexattr_destroy (&attributes
);
363 lock
->initialized
= 1;
368 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t
*lock
)
370 if (!lock
->initialized
)
374 err
= pthread_mutex_lock (&lock
->guard
);
377 if (!lock
->initialized
)
379 err
= glthread_recursive_lock_init_multithreaded (lock
);
382 pthread_mutex_unlock (&lock
->guard
);
386 err
= pthread_mutex_unlock (&lock
->guard
);
390 return pthread_mutex_lock (&lock
->recmutex
);
394 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t
*lock
)
396 if (!lock
->initialized
)
398 return pthread_mutex_unlock (&lock
->recmutex
);
402 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t
*lock
)
406 if (!lock
->initialized
)
408 err
= pthread_mutex_destroy (&lock
->recmutex
);
411 lock
->initialized
= 0;
420 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
424 err
= pthread_mutex_init (&lock
->mutex
, NULL
);
427 lock
->owner
= (pthread_t
) 0;
433 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t
*lock
)
435 pthread_t self
= pthread_self ();
436 if (lock
->owner
!= self
)
440 err
= pthread_mutex_lock (&lock
->mutex
);
445 if (++(lock
->depth
) == 0) /* wraparound? */
454 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t
*lock
)
456 if (lock
->owner
!= pthread_self ())
458 if (lock
->depth
== 0)
460 if (--(lock
->depth
) == 0)
462 lock
->owner
= (pthread_t
) 0;
463 return pthread_mutex_unlock (&lock
->mutex
);
470 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t
*lock
)
472 if (lock
->owner
!= (pthread_t
) 0)
474 return pthread_mutex_destroy (&lock
->mutex
);
479 /* -------------------------- gl_once_t datatype -------------------------- */
481 static const pthread_once_t fresh_once
= PTHREAD_ONCE_INIT
;
484 glthread_once_singlethreaded (pthread_once_t
*once_control
)
486 /* We don't know whether pthread_once_t is an integer type, a floating-point
487 type, a pointer type, or a structure type. */
488 char *firstbyte
= (char *)once_control
;
489 if (*firstbyte
== *(const char *)&fresh_once
)
491 /* First time use of once_control. Invert the first byte. */
492 *firstbyte
= ~ *(const char *)&fresh_once
;
501 /* ========================================================================= */
505 /* Use the GNU Pth threads library. */
507 /* -------------------------- gl_lock_t datatype -------------------------- */
509 /* ------------------------- gl_rwlock_t datatype ------------------------- */
511 # if !HAVE_PTH_RWLOCK_ACQUIRE_PREFER_WRITER
514 glthread_rwlock_init_multithreaded (gl_rwlock_t
*lock
)
516 if (!pth_mutex_init (&lock
->lock
))
518 if (!pth_cond_init (&lock
->waiting_readers
))
520 if (!pth_cond_init (&lock
->waiting_writers
))
522 lock
->waiting_writers_count
= 0;
524 lock
->initialized
= 1;
529 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t
*lock
)
531 if (!lock
->initialized
)
532 glthread_rwlock_init_multithreaded (lock
);
533 if (!pth_mutex_acquire (&lock
->lock
, 0, NULL
))
535 /* Test whether only readers are currently running, and whether the runcount
536 field will not overflow, and whether no writer is waiting. The latter
537 condition is because POSIX recommends that "write locks shall take
538 precedence over read locks", to avoid "writer starvation". */
539 while (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers_count
== 0))
541 /* This thread has to wait for a while. Enqueue it among the
543 if (!pth_cond_await (&lock
->waiting_readers
, &lock
->lock
, NULL
))
546 pth_mutex_release (&lock
->lock
);
551 return (!pth_mutex_release (&lock
->lock
) ? errno
: 0);
555 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t
*lock
)
557 if (!lock
->initialized
)
558 glthread_rwlock_init_multithreaded (lock
);
559 if (!pth_mutex_acquire (&lock
->lock
, 0, NULL
))
561 /* Test whether no readers or writers are currently running. */
562 while (!(lock
->runcount
== 0))
564 /* This thread has to wait for a while. Enqueue it among the
566 lock
->waiting_writers_count
++;
567 if (!pth_cond_await (&lock
->waiting_writers
, &lock
->lock
, NULL
))
570 lock
->waiting_writers_count
--;
571 pth_mutex_release (&lock
->lock
);
574 lock
->waiting_writers_count
--;
576 lock
->runcount
--; /* runcount becomes -1 */
577 return (!pth_mutex_release (&lock
->lock
) ? errno
: 0);
581 glthread_rwlock_unlock_multithreaded (gl_rwlock_t
*lock
)
585 if (!lock
->initialized
)
587 if (!pth_mutex_acquire (&lock
->lock
, 0, NULL
))
589 if (lock
->runcount
< 0)
591 /* Drop a writer lock. */
592 if (!(lock
->runcount
== -1))
594 pth_mutex_release (&lock
->lock
);
601 /* Drop a reader lock. */
602 if (!(lock
->runcount
> 0))
604 pth_mutex_release (&lock
->lock
);
609 if (lock
->runcount
== 0)
611 /* POSIX recommends that "write locks shall take precedence over read
612 locks", to avoid "writer starvation". */
613 if (lock
->waiting_writers_count
> 0)
615 /* Wake up one of the waiting writers. */
616 if (!pth_cond_notify (&lock
->waiting_writers
, FALSE
))
619 pth_mutex_release (&lock
->lock
);
625 /* Wake up all waiting readers. */
626 if (!pth_cond_notify (&lock
->waiting_readers
, TRUE
))
629 pth_mutex_release (&lock
->lock
);
634 return (!pth_mutex_release (&lock
->lock
) ? errno
: 0);
638 glthread_rwlock_destroy_multithreaded (gl_rwlock_t
*lock
)
640 lock
->initialized
= 0;
646 /* --------------------- gl_recursive_lock_t datatype --------------------- */
648 /* -------------------------- gl_once_t datatype -------------------------- */
651 glthread_once_call (void *arg
)
653 void (**gl_once_temp_addr
) (void) = (void (**) (void)) arg
;
654 void (*initfunction
) (void) = *gl_once_temp_addr
;
659 glthread_once_multithreaded (pth_once_t
*once_control
, void (*initfunction
) (void))
661 void (*temp
) (void) = initfunction
;
662 return (!pth_once (once_control
, glthread_once_call
, &temp
) ? errno
: 0);
666 glthread_once_singlethreaded (pth_once_t
*once_control
)
668 /* We know that pth_once_t is an integer type. */
669 if (*once_control
== PTH_ONCE_INIT
)
671 /* First time use of once_control. Invert the marker. */
672 *once_control
= ~ PTH_ONCE_INIT
;
681 /* ========================================================================= */
683 #if USE_SOLARIS_THREADS
685 /* Use the old Solaris threads library. */
687 /* -------------------------- gl_lock_t datatype -------------------------- */
689 /* ------------------------- gl_rwlock_t datatype ------------------------- */
691 /* --------------------- gl_recursive_lock_t datatype --------------------- */
694 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
698 err
= mutex_init (&lock
->mutex
, USYNC_THREAD
, NULL
);
701 lock
->owner
= (thread_t
) 0;
707 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t
*lock
)
709 thread_t self
= thr_self ();
710 if (lock
->owner
!= self
)
714 err
= mutex_lock (&lock
->mutex
);
719 if (++(lock
->depth
) == 0) /* wraparound? */
728 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t
*lock
)
730 if (lock
->owner
!= thr_self ())
732 if (lock
->depth
== 0)
734 if (--(lock
->depth
) == 0)
736 lock
->owner
= (thread_t
) 0;
737 return mutex_unlock (&lock
->mutex
);
744 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t
*lock
)
746 if (lock
->owner
!= (thread_t
) 0)
748 return mutex_destroy (&lock
->mutex
);
751 /* -------------------------- gl_once_t datatype -------------------------- */
754 glthread_once_multithreaded (gl_once_t
*once_control
, void (*initfunction
) (void))
756 if (!once_control
->inited
)
760 /* Use the mutex to guarantee that if another thread is already calling
761 the initfunction, this thread waits until it's finished. */
762 err
= mutex_lock (&once_control
->mutex
);
765 if (!once_control
->inited
)
767 once_control
->inited
= 1;
770 return mutex_unlock (&once_control
->mutex
);
777 glthread_once_singlethreaded (gl_once_t
*once_control
)
779 /* We know that gl_once_t contains an integer type. */
780 if (!once_control
->inited
)
782 /* First time use of once_control. Invert the marker. */
783 once_control
->inited
= ~ 0;
792 /* ========================================================================= */
794 #if USE_WINDOWS_THREADS
796 /* -------------------------- gl_lock_t datatype -------------------------- */
799 glthread_lock_init_func (gl_lock_t
*lock
)
801 InitializeCriticalSection (&lock
->lock
);
802 lock
->guard
.done
= 1;
806 glthread_lock_lock_func (gl_lock_t
*lock
)
808 if (!lock
->guard
.done
)
810 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
811 /* This thread is the first one to need this lock. Initialize it. */
812 glthread_lock_init (lock
);
814 /* Yield the CPU while waiting for another thread to finish
815 initializing this lock. */
816 while (!lock
->guard
.done
)
819 EnterCriticalSection (&lock
->lock
);
824 glthread_lock_unlock_func (gl_lock_t
*lock
)
826 if (!lock
->guard
.done
)
828 LeaveCriticalSection (&lock
->lock
);
833 glthread_lock_destroy_func (gl_lock_t
*lock
)
835 if (!lock
->guard
.done
)
837 DeleteCriticalSection (&lock
->lock
);
838 lock
->guard
.done
= 0;
842 /* ------------------------- gl_rwlock_t datatype ------------------------- */
844 /* In this file, the waitqueues are implemented as circular arrays. */
845 #define gl_waitqueue_t gl_carray_waitqueue_t
848 gl_waitqueue_init (gl_waitqueue_t
*wq
)
856 /* Enqueues the current thread, represented by an event, in a wait queue.
857 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
859 gl_waitqueue_add (gl_waitqueue_t
*wq
)
864 if (wq
->count
== wq
->alloc
)
866 unsigned int new_alloc
= 2 * wq
->alloc
+ 1;
868 (HANDLE
*) realloc (wq
->array
, new_alloc
* sizeof (HANDLE
));
869 if (new_array
== NULL
)
870 /* No more memory. */
871 return INVALID_HANDLE_VALUE
;
872 /* Now is a good opportunity to rotate the array so that its contents
873 starts at offset 0. */
876 unsigned int old_count
= wq
->count
;
877 unsigned int old_alloc
= wq
->alloc
;
878 unsigned int old_offset
= wq
->offset
;
880 if (old_offset
+ old_count
> old_alloc
)
882 unsigned int limit
= old_offset
+ old_count
- old_alloc
;
883 for (i
= 0; i
< limit
; i
++)
884 new_array
[old_alloc
+ i
] = new_array
[i
];
886 for (i
= 0; i
< old_count
; i
++)
887 new_array
[i
] = new_array
[old_offset
+ i
];
890 wq
->array
= new_array
;
891 wq
->alloc
= new_alloc
;
893 /* Whether the created event is a manual-reset one or an auto-reset one,
894 does not matter, since we will wait on it only once. */
895 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
896 if (event
== INVALID_HANDLE_VALUE
)
897 /* No way to allocate an event. */
898 return INVALID_HANDLE_VALUE
;
899 index
= wq
->offset
+ wq
->count
;
900 if (index
>= wq
->alloc
)
902 wq
->array
[index
] = event
;
907 /* Notifies the first thread from a wait queue and dequeues it. */
909 gl_waitqueue_notify_first (gl_waitqueue_t
*wq
)
911 SetEvent (wq
->array
[wq
->offset
+ 0]);
914 if (wq
->count
== 0 || wq
->offset
== wq
->alloc
)
918 /* Notifies all threads from a wait queue and dequeues them all. */
920 gl_waitqueue_notify_all (gl_waitqueue_t
*wq
)
924 for (i
= 0; i
< wq
->count
; i
++)
926 unsigned int index
= wq
->offset
+ i
;
927 if (index
>= wq
->alloc
)
929 SetEvent (wq
->array
[index
]);
936 glthread_rwlock_init_func (gl_rwlock_t
*lock
)
938 InitializeCriticalSection (&lock
->lock
);
939 gl_waitqueue_init (&lock
->waiting_readers
);
940 gl_waitqueue_init (&lock
->waiting_writers
);
942 lock
->guard
.done
= 1;
946 glthread_rwlock_rdlock_func (gl_rwlock_t
*lock
)
948 if (!lock
->guard
.done
)
950 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
951 /* This thread is the first one to need this lock. Initialize it. */
952 glthread_rwlock_init (lock
);
954 /* Yield the CPU while waiting for another thread to finish
955 initializing this lock. */
956 while (!lock
->guard
.done
)
959 EnterCriticalSection (&lock
->lock
);
960 /* Test whether only readers are currently running, and whether the runcount
961 field will not overflow, and whether no writer is waiting. The latter
962 condition is because POSIX recommends that "write locks shall take
963 precedence over read locks", to avoid "writer starvation". */
964 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
966 /* This thread has to wait for a while. Enqueue it among the
968 HANDLE event
= gl_waitqueue_add (&lock
->waiting_readers
);
969 if (event
!= INVALID_HANDLE_VALUE
)
972 LeaveCriticalSection (&lock
->lock
);
973 /* Wait until another thread signals this event. */
974 result
= WaitForSingleObject (event
, INFINITE
);
975 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
978 /* The thread which signalled the event already did the bookkeeping:
979 removed us from the waiting_readers, incremented lock->runcount. */
980 if (!(lock
->runcount
> 0))
986 /* Allocation failure. Weird. */
989 LeaveCriticalSection (&lock
->lock
);
991 EnterCriticalSection (&lock
->lock
);
993 while (!(lock
->runcount
+ 1 > 0));
997 LeaveCriticalSection (&lock
->lock
);
1002 glthread_rwlock_wrlock_func (gl_rwlock_t
*lock
)
1004 if (!lock
->guard
.done
)
1006 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
1007 /* This thread is the first one to need this lock. Initialize it. */
1008 glthread_rwlock_init (lock
);
1010 /* Yield the CPU while waiting for another thread to finish
1011 initializing this lock. */
1012 while (!lock
->guard
.done
)
1015 EnterCriticalSection (&lock
->lock
);
1016 /* Test whether no readers or writers are currently running. */
1017 if (!(lock
->runcount
== 0))
1019 /* This thread has to wait for a while. Enqueue it among the
1021 HANDLE event
= gl_waitqueue_add (&lock
->waiting_writers
);
1022 if (event
!= INVALID_HANDLE_VALUE
)
1025 LeaveCriticalSection (&lock
->lock
);
1026 /* Wait until another thread signals this event. */
1027 result
= WaitForSingleObject (event
, INFINITE
);
1028 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
1030 CloseHandle (event
);
1031 /* The thread which signalled the event already did the bookkeeping:
1032 removed us from the waiting_writers, set lock->runcount = -1. */
1033 if (!(lock
->runcount
== -1))
1039 /* Allocation failure. Weird. */
1042 LeaveCriticalSection (&lock
->lock
);
1044 EnterCriticalSection (&lock
->lock
);
1046 while (!(lock
->runcount
== 0));
1049 lock
->runcount
--; /* runcount becomes -1 */
1050 LeaveCriticalSection (&lock
->lock
);
1055 glthread_rwlock_unlock_func (gl_rwlock_t
*lock
)
1057 if (!lock
->guard
.done
)
1059 EnterCriticalSection (&lock
->lock
);
1060 if (lock
->runcount
< 0)
1062 /* Drop a writer lock. */
1063 if (!(lock
->runcount
== -1))
1069 /* Drop a reader lock. */
1070 if (!(lock
->runcount
> 0))
1072 LeaveCriticalSection (&lock
->lock
);
1077 if (lock
->runcount
== 0)
1079 /* POSIX recommends that "write locks shall take precedence over read
1080 locks", to avoid "writer starvation". */
1081 if (lock
->waiting_writers
.count
> 0)
1083 /* Wake up one of the waiting writers. */
1085 gl_waitqueue_notify_first (&lock
->waiting_writers
);
1089 /* Wake up all waiting readers. */
1090 lock
->runcount
+= lock
->waiting_readers
.count
;
1091 gl_waitqueue_notify_all (&lock
->waiting_readers
);
1094 LeaveCriticalSection (&lock
->lock
);
1099 glthread_rwlock_destroy_func (gl_rwlock_t
*lock
)
1101 if (!lock
->guard
.done
)
1103 if (lock
->runcount
!= 0)
1105 DeleteCriticalSection (&lock
->lock
);
1106 if (lock
->waiting_readers
.array
!= NULL
)
1107 free (lock
->waiting_readers
.array
);
1108 if (lock
->waiting_writers
.array
!= NULL
)
1109 free (lock
->waiting_writers
.array
);
1110 lock
->guard
.done
= 0;
1114 /* --------------------- gl_recursive_lock_t datatype --------------------- */
1117 glthread_recursive_lock_init_func (gl_recursive_lock_t
*lock
)
1121 InitializeCriticalSection (&lock
->lock
);
1122 lock
->guard
.done
= 1;
1126 glthread_recursive_lock_lock_func (gl_recursive_lock_t
*lock
)
1128 if (!lock
->guard
.done
)
1130 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
1131 /* This thread is the first one to need this lock. Initialize it. */
1132 glthread_recursive_lock_init (lock
);
1134 /* Yield the CPU while waiting for another thread to finish
1135 initializing this lock. */
1136 while (!lock
->guard
.done
)
1140 DWORD self
= GetCurrentThreadId ();
1141 if (lock
->owner
!= self
)
1143 EnterCriticalSection (&lock
->lock
);
1146 if (++(lock
->depth
) == 0) /* wraparound? */
1156 glthread_recursive_lock_unlock_func (gl_recursive_lock_t
*lock
)
1158 if (lock
->owner
!= GetCurrentThreadId ())
1160 if (lock
->depth
== 0)
1162 if (--(lock
->depth
) == 0)
1165 LeaveCriticalSection (&lock
->lock
);
1171 glthread_recursive_lock_destroy_func (gl_recursive_lock_t
*lock
)
1173 if (lock
->owner
!= 0)
1175 DeleteCriticalSection (&lock
->lock
);
1176 lock
->guard
.done
= 0;
1180 /* -------------------------- gl_once_t datatype -------------------------- */
1183 glthread_once_func (gl_once_t
*once_control
, void (*initfunction
) (void))
1185 if (once_control
->inited
<= 0)
1187 if (InterlockedIncrement (&once_control
->started
) == 0)
1189 /* This thread is the first one to come to this once_control. */
1190 InitializeCriticalSection (&once_control
->lock
);
1191 EnterCriticalSection (&once_control
->lock
);
1192 once_control
->inited
= 0;
1194 once_control
->inited
= 1;
1195 LeaveCriticalSection (&once_control
->lock
);
1199 /* Undo last operation. */
1200 InterlockedDecrement (&once_control
->started
);
1201 /* Some other thread has already started the initialization.
1202 Yield the CPU while waiting for the other thread to finish
1203 initializing and taking the lock. */
1204 while (once_control
->inited
< 0)
1206 if (once_control
->inited
<= 0)
1208 /* Take the lock. This blocks until the other thread has
1209 finished calling the initfunction. */
1210 EnterCriticalSection (&once_control
->lock
);
1211 LeaveCriticalSection (&once_control
->lock
);
1212 if (!(once_control
->inited
> 0))
1221 /* ========================================================================= */