1 /* Timed read-write locks (native Windows implementation).
2 Copyright (C) 2005-2020 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 2, 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>, 2019. */
22 #include "windows-timedrwlock.h"
29 /* Don't assume that UNICODE is not defined. */
31 #define CreateEvent CreateEventA
33 /* In this file, the waitqueues are implemented as linked lists. */
34 #define glwthread_waitqueue_t glwthread_clinked_waitqueue_t
36 /* All links of a circular list, except the anchor, are of this type, carrying
38 struct glwthread_waitqueue_element
40 struct glwthread_waitqueue_link link
; /* must be the first field! */
41 HANDLE event
; /* Waiting thread, represented by an event.
42 This field is immutable once initialized. */
46 glwthread_waitqueue_init (glwthread_waitqueue_t
*wq
)
48 wq
->wq_list
.wql_next
= &wq
->wq_list
;
49 wq
->wq_list
.wql_prev
= &wq
->wq_list
;
53 /* Enqueues the current thread, represented by an event, in a wait queue.
54 Returns NULL if an allocation failure occurs. */
55 static struct glwthread_waitqueue_element
*
56 glwthread_waitqueue_add (glwthread_waitqueue_t
*wq
)
58 struct glwthread_waitqueue_element
*elt
;
61 /* Allocate the memory for the waitqueue element on the heap, not on the
62 thread's stack. If the thread exits unexpectedly, we prefer to leak
63 some memory rather than to access unavailable memory and crash. */
65 (struct glwthread_waitqueue_element
*)
66 malloc (sizeof (struct glwthread_waitqueue_element
));
71 /* Whether the created event is a manual-reset one or an auto-reset one,
72 does not matter, since we will wait on it only once. */
73 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
74 if (event
== INVALID_HANDLE_VALUE
)
76 /* No way to allocate an event. */
81 /* Insert elt at the end of the circular list. */
82 (elt
->link
.wql_prev
= wq
->wq_list
.wql_prev
)->wql_next
= &elt
->link
;
83 (elt
->link
.wql_next
= &wq
->wq_list
)->wql_prev
= &elt
->link
;
88 /* Removes the current thread, represented by a
89 'struct glwthread_waitqueue_element *', from a wait queue.
90 Returns true if is was found and removed, false if it was not present. */
92 glwthread_waitqueue_remove (glwthread_waitqueue_t
*wq
,
93 struct glwthread_waitqueue_element
*elt
)
95 if (elt
->link
.wql_next
!= NULL
&& elt
->link
.wql_prev
!= NULL
)
97 /* Remove elt from the circular list. */
98 struct glwthread_waitqueue_link
*prev
= elt
->link
.wql_prev
;
99 struct glwthread_waitqueue_link
*next
= elt
->link
.wql_next
;
100 prev
->wql_next
= next
;
101 next
->wql_prev
= prev
;
102 elt
->link
.wql_next
= NULL
;
103 elt
->link
.wql_prev
= NULL
;
111 /* Notifies the first thread from a wait queue and dequeues it. */
113 glwthread_waitqueue_notify_first (glwthread_waitqueue_t
*wq
)
115 if (wq
->wq_list
.wql_next
!= &wq
->wq_list
)
117 struct glwthread_waitqueue_element
*elt
=
118 (struct glwthread_waitqueue_element
*) wq
->wq_list
.wql_next
;
119 struct glwthread_waitqueue_link
*prev
;
120 struct glwthread_waitqueue_link
*next
;
122 /* Remove elt from the circular list. */
123 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
124 next
= elt
->link
.wql_next
;
125 prev
->wql_next
= next
;
126 next
->wql_prev
= prev
;
127 elt
->link
.wql_next
= NULL
;
128 elt
->link
.wql_prev
= NULL
;
131 SetEvent (elt
->event
);
132 /* After the SetEvent, this thread cannot access *elt any more, because
133 the woken-up thread will quickly call free (elt). */
137 /* Notifies all threads from a wait queue and dequeues them all. */
139 glwthread_waitqueue_notify_all (glwthread_waitqueue_t
*wq
)
141 struct glwthread_waitqueue_link
*l
;
143 for (l
= wq
->wq_list
.wql_next
; l
!= &wq
->wq_list
; )
145 struct glwthread_waitqueue_element
*elt
=
146 (struct glwthread_waitqueue_element
*) l
;
147 struct glwthread_waitqueue_link
*prev
;
148 struct glwthread_waitqueue_link
*next
;
150 /* Remove elt from the circular list. */
151 prev
= &wq
->wq_list
; /* = elt->link.wql_prev; */
152 next
= elt
->link
.wql_next
;
153 prev
->wql_next
= next
;
154 next
->wql_prev
= prev
;
155 elt
->link
.wql_next
= NULL
;
156 elt
->link
.wql_prev
= NULL
;
159 SetEvent (elt
->event
);
160 /* After the SetEvent, this thread cannot access *elt any more, because
161 the woken-up thread will quickly call free (elt). */
165 if (!(wq
->wq_list
.wql_next
== &wq
->wq_list
166 && wq
->wq_list
.wql_prev
== &wq
->wq_list
172 glwthread_timedrwlock_init (glwthread_timedrwlock_t
*lock
)
174 InitializeCriticalSection (&lock
->lock
);
175 glwthread_waitqueue_init (&lock
->waiting_readers
);
176 glwthread_waitqueue_init (&lock
->waiting_writers
);
178 lock
->guard
.done
= 1;
182 glwthread_timedrwlock_rdlock (glwthread_timedrwlock_t
*lock
)
184 if (!lock
->guard
.done
)
186 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
187 /* This thread is the first one to need this lock. Initialize it. */
188 glwthread_timedrwlock_init (lock
);
191 /* Don't let lock->guard.started grow and wrap around. */
192 InterlockedDecrement (&lock
->guard
.started
);
193 /* Yield the CPU while waiting for another thread to finish
194 initializing this lock. */
195 while (!lock
->guard
.done
)
199 EnterCriticalSection (&lock
->lock
);
200 /* Test whether only readers are currently running, and whether the runcount
201 field will not overflow, and whether no writer is waiting. The latter
202 condition is because POSIX recommends that "write locks shall take
203 precedence over read locks", to avoid "writer starvation". */
204 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
206 /* This thread has to wait for a while. Enqueue it among the
208 struct glwthread_waitqueue_element
*elt
=
209 glwthread_waitqueue_add (&lock
->waiting_readers
);
212 HANDLE event
= elt
->event
;
214 LeaveCriticalSection (&lock
->lock
);
215 /* Wait until another thread signals this event. */
216 result
= WaitForSingleObject (event
, INFINITE
);
217 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
221 /* The thread which signalled the event already did the bookkeeping:
222 removed us from the waiting_readers, incremented lock->runcount. */
223 if (!(lock
->runcount
> 0))
229 /* Allocation failure. Weird. */
232 LeaveCriticalSection (&lock
->lock
);
234 EnterCriticalSection (&lock
->lock
);
236 while (!(lock
->runcount
+ 1 > 0));
240 LeaveCriticalSection (&lock
->lock
);
245 glwthread_timedrwlock_wrlock (glwthread_timedrwlock_t
*lock
)
247 if (!lock
->guard
.done
)
249 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
250 /* This thread is the first one to need this lock. Initialize it. */
251 glwthread_timedrwlock_init (lock
);
254 /* Don't let lock->guard.started grow and wrap around. */
255 InterlockedDecrement (&lock
->guard
.started
);
256 /* Yield the CPU while waiting for another thread to finish
257 initializing this lock. */
258 while (!lock
->guard
.done
)
262 EnterCriticalSection (&lock
->lock
);
263 /* Test whether no readers or writers are currently running. */
264 if (!(lock
->runcount
== 0))
266 /* This thread has to wait for a while. Enqueue it among the
268 struct glwthread_waitqueue_element
*elt
=
269 glwthread_waitqueue_add (&lock
->waiting_writers
);
272 HANDLE event
= elt
->event
;
274 LeaveCriticalSection (&lock
->lock
);
275 /* Wait until another thread signals this event. */
276 result
= WaitForSingleObject (event
, INFINITE
);
277 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
281 /* The thread which signalled the event already did the bookkeeping:
282 removed us from the waiting_writers, set lock->runcount = -1. */
283 if (!(lock
->runcount
== -1))
289 /* Allocation failure. Weird. */
292 LeaveCriticalSection (&lock
->lock
);
294 EnterCriticalSection (&lock
->lock
);
296 while (!(lock
->runcount
== 0));
299 lock
->runcount
--; /* runcount becomes -1 */
300 LeaveCriticalSection (&lock
->lock
);
305 glwthread_timedrwlock_tryrdlock (glwthread_timedrwlock_t
*lock
)
307 if (!lock
->guard
.done
)
309 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
310 /* This thread is the first one to need this lock. Initialize it. */
311 glwthread_timedrwlock_init (lock
);
314 /* Don't let lock->guard.started grow and wrap around. */
315 InterlockedDecrement (&lock
->guard
.started
);
316 /* Yield the CPU while waiting for another thread to finish
317 initializing this lock. */
318 while (!lock
->guard
.done
)
322 /* It's OK to wait for this critical section, because it is never taken for a
324 EnterCriticalSection (&lock
->lock
);
325 /* Test whether only readers are currently running, and whether the runcount
326 field will not overflow, and whether no writer is waiting. The latter
327 condition is because POSIX recommends that "write locks shall take
328 precedence over read locks", to avoid "writer starvation". */
329 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
331 /* This thread would have to wait for a while. Return instead. */
332 LeaveCriticalSection (&lock
->lock
);
336 LeaveCriticalSection (&lock
->lock
);
341 glwthread_timedrwlock_trywrlock (glwthread_timedrwlock_t
*lock
)
343 if (!lock
->guard
.done
)
345 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
346 /* This thread is the first one to need this lock. Initialize it. */
347 glwthread_timedrwlock_init (lock
);
350 /* Don't let lock->guard.started grow and wrap around. */
351 InterlockedDecrement (&lock
->guard
.started
);
352 /* Yield the CPU while waiting for another thread to finish
353 initializing this lock. */
354 while (!lock
->guard
.done
)
358 /* It's OK to wait for this critical section, because it is never taken for a
360 EnterCriticalSection (&lock
->lock
);
361 /* Test whether no readers or writers are currently running. */
362 if (!(lock
->runcount
== 0))
364 /* This thread would have to wait for a while. Return instead. */
365 LeaveCriticalSection (&lock
->lock
);
368 lock
->runcount
--; /* runcount becomes -1 */
369 LeaveCriticalSection (&lock
->lock
);
374 glwthread_timedrwlock_timedrdlock (glwthread_timedrwlock_t
*lock
,
375 const struct timespec
*abstime
)
377 if (!lock
->guard
.done
)
379 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
380 /* This thread is the first one to need this lock. Initialize it. */
381 glwthread_timedrwlock_init (lock
);
384 /* Don't let lock->guard.started grow and wrap around. */
385 InterlockedDecrement (&lock
->guard
.started
);
386 /* Yield the CPU while waiting for another thread to finish
387 initializing this lock. */
388 while (!lock
->guard
.done
)
392 EnterCriticalSection (&lock
->lock
);
393 /* Test whether only readers are currently running, and whether the runcount
394 field will not overflow, and whether no writer is waiting. The latter
395 condition is because POSIX recommends that "write locks shall take
396 precedence over read locks", to avoid "writer starvation". */
397 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
399 /* This thread has to wait for a while. Enqueue it among the
401 struct glwthread_waitqueue_element
*elt
=
402 glwthread_waitqueue_add (&lock
->waiting_readers
);
405 HANDLE event
= elt
->event
;
406 struct timeval currtime
;
411 LeaveCriticalSection (&lock
->lock
);
413 gettimeofday (&currtime
, NULL
);
415 /* Wait until another thread signals this event or until the
417 if (currtime
.tv_sec
> abstime
->tv_sec
)
421 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
422 timeout
= seconds
* 1000;
423 if (timeout
/ 1000 != seconds
) /* overflow? */
428 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
429 if (milliseconds
>= 0)
431 timeout
+= milliseconds
;
432 if (timeout
< milliseconds
) /* overflow? */
437 if (timeout
>= - milliseconds
)
438 timeout
-= (- milliseconds
);
446 /* WaitForSingleObject
447 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
448 result
= WaitForSingleObject (event
, timeout
);
449 if (result
== WAIT_FAILED
)
451 if (result
!= WAIT_TIMEOUT
)
455 /* The thread which signalled the event already did the
456 bookkeeping: removed us from the waiting_readers,
457 incremented lock->runcount. */
458 if (!(lock
->runcount
> 0))
463 EnterCriticalSection (&lock
->lock
);
464 /* Remove ourselves from the waiting_readers. */
465 if (glwthread_waitqueue_remove (&lock
->waiting_readers
, elt
))
468 /* The event was signalled just now. */
470 LeaveCriticalSection (&lock
->lock
);
474 /* Same assertion as above. */
475 if (!(lock
->runcount
> 0))
481 /* Allocation failure. Weird. */
484 LeaveCriticalSection (&lock
->lock
);
486 EnterCriticalSection (&lock
->lock
);
488 while (!(lock
->runcount
+ 1 > 0));
492 LeaveCriticalSection (&lock
->lock
);
497 glwthread_timedrwlock_timedwrlock (glwthread_timedrwlock_t
*lock
,
498 const struct timespec
*abstime
)
500 if (!lock
->guard
.done
)
502 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
503 /* This thread is the first one to need this lock. Initialize it. */
504 glwthread_timedrwlock_init (lock
);
507 /* Don't let lock->guard.started grow and wrap around. */
508 InterlockedDecrement (&lock
->guard
.started
);
509 /* Yield the CPU while waiting for another thread to finish
510 initializing this lock. */
511 while (!lock
->guard
.done
)
515 EnterCriticalSection (&lock
->lock
);
516 /* Test whether no readers or writers are currently running. */
517 if (!(lock
->runcount
== 0))
519 /* This thread has to wait for a while. Enqueue it among the
521 struct glwthread_waitqueue_element
*elt
=
522 glwthread_waitqueue_add (&lock
->waiting_writers
);
525 HANDLE event
= elt
->event
;
526 struct timeval currtime
;
531 LeaveCriticalSection (&lock
->lock
);
533 gettimeofday (&currtime
, NULL
);
535 /* Wait until another thread signals this event or until the
537 if (currtime
.tv_sec
> abstime
->tv_sec
)
541 unsigned long seconds
= abstime
->tv_sec
- currtime
.tv_sec
;
542 timeout
= seconds
* 1000;
543 if (timeout
/ 1000 != seconds
) /* overflow? */
548 abstime
->tv_nsec
/ 1000000 - currtime
.tv_usec
/ 1000;
549 if (milliseconds
>= 0)
551 timeout
+= milliseconds
;
552 if (timeout
< milliseconds
) /* overflow? */
557 if (timeout
>= - milliseconds
)
558 timeout
-= (- milliseconds
);
566 /* WaitForSingleObject
567 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
568 result
= WaitForSingleObject (event
, timeout
);
569 if (result
== WAIT_FAILED
)
571 if (result
!= WAIT_TIMEOUT
)
575 /* The thread which signalled the event already did the
576 bookkeeping: removed us from the waiting_writers, set
577 lock->runcount = -1. */
578 if (!(lock
->runcount
== -1))
583 EnterCriticalSection (&lock
->lock
);
584 /* Remove ourselves from the waiting_writers. */
585 if (glwthread_waitqueue_remove (&lock
->waiting_writers
, elt
))
588 /* The event was signalled just now. */
590 LeaveCriticalSection (&lock
->lock
);
594 /* Same assertion as above. */
595 if (!(lock
->runcount
== -1))
601 /* Allocation failure. Weird. */
604 LeaveCriticalSection (&lock
->lock
);
606 EnterCriticalSection (&lock
->lock
);
608 while (!(lock
->runcount
== 0));
611 lock
->runcount
--; /* runcount becomes -1 */
612 LeaveCriticalSection (&lock
->lock
);
617 glwthread_timedrwlock_unlock (glwthread_timedrwlock_t
*lock
)
619 if (!lock
->guard
.done
)
621 EnterCriticalSection (&lock
->lock
);
622 if (lock
->runcount
< 0)
624 /* Drop a writer lock. */
625 if (!(lock
->runcount
== -1))
631 /* Drop a reader lock. */
632 if (!(lock
->runcount
> 0))
634 LeaveCriticalSection (&lock
->lock
);
639 if (lock
->runcount
== 0)
641 /* POSIX recommends that "write locks shall take precedence over read
642 locks", to avoid "writer starvation". */
643 if (lock
->waiting_writers
.count
> 0)
645 /* Wake up one of the waiting writers. */
647 glwthread_waitqueue_notify_first (&lock
->waiting_writers
);
651 /* Wake up all waiting readers. */
652 lock
->runcount
+= lock
->waiting_readers
.count
;
653 glwthread_waitqueue_notify_all (&lock
->waiting_readers
);
656 LeaveCriticalSection (&lock
->lock
);
661 glwthread_timedrwlock_destroy (glwthread_timedrwlock_t
*lock
)
663 if (!lock
->guard
.done
)
665 if (lock
->runcount
!= 0)
667 DeleteCriticalSection (&lock
->lock
);
668 lock
->guard
.done
= 0;