1 /* 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>, 2005.
18 Based on GCC's gthr-win32.h. */
23 #include "windows-rwlock.h"
28 /* In this file, the waitqueues are implemented as circular arrays. */
29 #define glwthread_waitqueue_t glwthread_carray_waitqueue_t
32 glwthread_waitqueue_init (glwthread_waitqueue_t
*wq
)
40 /* Enqueues the current thread, represented by an event, in a wait queue.
41 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
43 glwthread_waitqueue_add (glwthread_waitqueue_t
*wq
)
48 if (wq
->count
== wq
->alloc
)
50 unsigned int new_alloc
= 2 * wq
->alloc
+ 1;
52 (HANDLE
*) realloc (wq
->array
, new_alloc
* sizeof (HANDLE
));
53 if (new_array
== NULL
)
55 return INVALID_HANDLE_VALUE
;
56 /* Now is a good opportunity to rotate the array so that its contents
57 starts at offset 0. */
60 unsigned int old_count
= wq
->count
;
61 unsigned int old_alloc
= wq
->alloc
;
62 unsigned int old_offset
= wq
->offset
;
64 if (old_offset
+ old_count
> old_alloc
)
66 unsigned int limit
= old_offset
+ old_count
- old_alloc
;
67 for (i
= 0; i
< limit
; i
++)
68 new_array
[old_alloc
+ i
] = new_array
[i
];
70 for (i
= 0; i
< old_count
; i
++)
71 new_array
[i
] = new_array
[old_offset
+ i
];
74 wq
->array
= new_array
;
75 wq
->alloc
= new_alloc
;
77 /* Whether the created event is a manual-reset one or an auto-reset one,
78 does not matter, since we will wait on it only once. */
79 event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
80 if (event
== INVALID_HANDLE_VALUE
)
81 /* No way to allocate an event. */
82 return INVALID_HANDLE_VALUE
;
83 index
= wq
->offset
+ wq
->count
;
84 if (index
>= wq
->alloc
)
86 wq
->array
[index
] = event
;
91 /* Notifies the first thread from a wait queue and dequeues it. */
93 glwthread_waitqueue_notify_first (glwthread_waitqueue_t
*wq
)
95 SetEvent (wq
->array
[wq
->offset
+ 0]);
98 if (wq
->count
== 0 || wq
->offset
== wq
->alloc
)
102 /* Notifies all threads from a wait queue and dequeues them all. */
104 glwthread_waitqueue_notify_all (glwthread_waitqueue_t
*wq
)
108 for (i
= 0; i
< wq
->count
; i
++)
110 unsigned int index
= wq
->offset
+ i
;
111 if (index
>= wq
->alloc
)
113 SetEvent (wq
->array
[index
]);
120 glwthread_rwlock_init (glwthread_rwlock_t
*lock
)
122 InitializeCriticalSection (&lock
->lock
);
123 glwthread_waitqueue_init (&lock
->waiting_readers
);
124 glwthread_waitqueue_init (&lock
->waiting_writers
);
126 lock
->guard
.done
= 1;
130 glwthread_rwlock_rdlock (glwthread_rwlock_t
*lock
)
132 if (!lock
->guard
.done
)
134 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
135 /* This thread is the first one to need this lock. Initialize it. */
136 glwthread_rwlock_init (lock
);
139 /* Don't let lock->guard.started grow and wrap around. */
140 InterlockedDecrement (&lock
->guard
.started
);
141 /* Yield the CPU while waiting for another thread to finish
142 initializing this lock. */
143 while (!lock
->guard
.done
)
147 EnterCriticalSection (&lock
->lock
);
148 /* Test whether only readers are currently running, and whether the runcount
149 field will not overflow, and whether no writer is waiting. The latter
150 condition is because POSIX recommends that "write locks shall take
151 precedence over read locks", to avoid "writer starvation". */
152 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
154 /* This thread has to wait for a while. Enqueue it among the
156 HANDLE event
= glwthread_waitqueue_add (&lock
->waiting_readers
);
157 if (event
!= INVALID_HANDLE_VALUE
)
160 LeaveCriticalSection (&lock
->lock
);
161 /* Wait until another thread signals this event. */
162 result
= WaitForSingleObject (event
, INFINITE
);
163 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
166 /* The thread which signalled the event already did the bookkeeping:
167 removed us from the waiting_readers, incremented lock->runcount. */
168 if (!(lock
->runcount
> 0))
174 /* Allocation failure. Weird. */
177 LeaveCriticalSection (&lock
->lock
);
179 EnterCriticalSection (&lock
->lock
);
181 while (!(lock
->runcount
+ 1 > 0));
185 LeaveCriticalSection (&lock
->lock
);
190 glwthread_rwlock_wrlock (glwthread_rwlock_t
*lock
)
192 if (!lock
->guard
.done
)
194 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
195 /* This thread is the first one to need this lock. Initialize it. */
196 glwthread_rwlock_init (lock
);
199 /* Don't let lock->guard.started grow and wrap around. */
200 InterlockedDecrement (&lock
->guard
.started
);
201 /* Yield the CPU while waiting for another thread to finish
202 initializing this lock. */
203 while (!lock
->guard
.done
)
207 EnterCriticalSection (&lock
->lock
);
208 /* Test whether no readers or writers are currently running. */
209 if (!(lock
->runcount
== 0))
211 /* This thread has to wait for a while. Enqueue it among the
213 HANDLE event
= glwthread_waitqueue_add (&lock
->waiting_writers
);
214 if (event
!= INVALID_HANDLE_VALUE
)
217 LeaveCriticalSection (&lock
->lock
);
218 /* Wait until another thread signals this event. */
219 result
= WaitForSingleObject (event
, INFINITE
);
220 if (result
== WAIT_FAILED
|| result
== WAIT_TIMEOUT
)
223 /* The thread which signalled the event already did the bookkeeping:
224 removed us from the waiting_writers, set lock->runcount = -1. */
225 if (!(lock
->runcount
== -1))
231 /* Allocation failure. Weird. */
234 LeaveCriticalSection (&lock
->lock
);
236 EnterCriticalSection (&lock
->lock
);
238 while (!(lock
->runcount
== 0));
241 lock
->runcount
--; /* runcount becomes -1 */
242 LeaveCriticalSection (&lock
->lock
);
247 glwthread_rwlock_tryrdlock (glwthread_rwlock_t
*lock
)
249 if (!lock
->guard
.done
)
251 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
252 /* This thread is the first one to need this lock. Initialize it. */
253 glwthread_rwlock_init (lock
);
256 /* Don't let lock->guard.started grow and wrap around. */
257 InterlockedDecrement (&lock
->guard
.started
);
258 /* Yield the CPU while waiting for another thread to finish
259 initializing this lock. */
260 while (!lock
->guard
.done
)
264 /* It's OK to wait for this critical section, because it is never taken for a
266 EnterCriticalSection (&lock
->lock
);
267 /* Test whether only readers are currently running, and whether the runcount
268 field will not overflow, and whether no writer is waiting. The latter
269 condition is because POSIX recommends that "write locks shall take
270 precedence over read locks", to avoid "writer starvation". */
271 if (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers
.count
== 0))
273 /* This thread would have to wait for a while. Return instead. */
274 LeaveCriticalSection (&lock
->lock
);
278 LeaveCriticalSection (&lock
->lock
);
283 glwthread_rwlock_trywrlock (glwthread_rwlock_t
*lock
)
285 if (!lock
->guard
.done
)
287 if (InterlockedIncrement (&lock
->guard
.started
) == 0)
288 /* This thread is the first one to need this lock. Initialize it. */
289 glwthread_rwlock_init (lock
);
292 /* Don't let lock->guard.started grow and wrap around. */
293 InterlockedDecrement (&lock
->guard
.started
);
294 /* Yield the CPU while waiting for another thread to finish
295 initializing this lock. */
296 while (!lock
->guard
.done
)
300 /* It's OK to wait for this critical section, because it is never taken for a
302 EnterCriticalSection (&lock
->lock
);
303 /* Test whether no readers or writers are currently running. */
304 if (!(lock
->runcount
== 0))
306 /* This thread would have to wait for a while. Return instead. */
307 LeaveCriticalSection (&lock
->lock
);
310 lock
->runcount
--; /* runcount becomes -1 */
311 LeaveCriticalSection (&lock
->lock
);
316 glwthread_rwlock_unlock (glwthread_rwlock_t
*lock
)
318 if (!lock
->guard
.done
)
320 EnterCriticalSection (&lock
->lock
);
321 if (lock
->runcount
< 0)
323 /* Drop a writer lock. */
324 if (!(lock
->runcount
== -1))
330 /* Drop a reader lock. */
331 if (!(lock
->runcount
> 0))
333 LeaveCriticalSection (&lock
->lock
);
338 if (lock
->runcount
== 0)
340 /* POSIX recommends that "write locks shall take precedence over read
341 locks", to avoid "writer starvation". */
342 if (lock
->waiting_writers
.count
> 0)
344 /* Wake up one of the waiting writers. */
346 glwthread_waitqueue_notify_first (&lock
->waiting_writers
);
350 /* Wake up all waiting readers. */
351 lock
->runcount
+= lock
->waiting_readers
.count
;
352 glwthread_waitqueue_notify_all (&lock
->waiting_readers
);
355 LeaveCriticalSection (&lock
->lock
);
360 glwthread_rwlock_destroy (glwthread_rwlock_t
*lock
)
362 if (!lock
->guard
.done
)
364 if (lock
->runcount
!= 0)
366 DeleteCriticalSection (&lock
->lock
);
367 if (lock
->waiting_readers
.array
!= NULL
)
368 free (lock
->waiting_readers
.array
);
369 if (lock
->waiting_writers
.array
!= NULL
)
370 free (lock
->waiting_writers
.array
);
371 lock
->guard
.done
= 0;