1*820c1a8dSHiroo HAYASHI /* Read-write locks (native Windows implementation).
2*820c1a8dSHiroo HAYASHI Copyright (C) 2005-2021 Free Software Foundation, Inc.
3*820c1a8dSHiroo HAYASHI
4*820c1a8dSHiroo HAYASHI This file is free software: you can redistribute it and/or modify
5*820c1a8dSHiroo HAYASHI it under the terms of the GNU Lesser General Public License as
6*820c1a8dSHiroo HAYASHI published by the Free Software Foundation; either version 2.1 of the
7*820c1a8dSHiroo HAYASHI License, or (at your option) any later version.
8*820c1a8dSHiroo HAYASHI
9*820c1a8dSHiroo HAYASHI This file is distributed in the hope that it will be useful,
10*820c1a8dSHiroo HAYASHI but WITHOUT ANY WARRANTY; without even the implied warranty of
11*820c1a8dSHiroo HAYASHI MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*820c1a8dSHiroo HAYASHI GNU Lesser General Public License for more details.
13*820c1a8dSHiroo HAYASHI
14*820c1a8dSHiroo HAYASHI You should have received a copy of the GNU Lesser General Public License
15*820c1a8dSHiroo HAYASHI along with this program. If not, see <https://www.gnu.org/licenses/>. */
16*820c1a8dSHiroo HAYASHI
17*820c1a8dSHiroo HAYASHI /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18*820c1a8dSHiroo HAYASHI Based on GCC's gthr-win32.h. */
19*820c1a8dSHiroo HAYASHI
20*820c1a8dSHiroo HAYASHI #include <config.h>
21*820c1a8dSHiroo HAYASHI
22*820c1a8dSHiroo HAYASHI /* Specification. */
23*820c1a8dSHiroo HAYASHI #include "windows-rwlock.h"
24*820c1a8dSHiroo HAYASHI
25*820c1a8dSHiroo HAYASHI #include <errno.h>
26*820c1a8dSHiroo HAYASHI #include <stdlib.h>
27*820c1a8dSHiroo HAYASHI
28*820c1a8dSHiroo HAYASHI /* Don't assume that UNICODE is not defined. */
29*820c1a8dSHiroo HAYASHI #undef CreateEvent
30*820c1a8dSHiroo HAYASHI #define CreateEvent CreateEventA
31*820c1a8dSHiroo HAYASHI
32*820c1a8dSHiroo HAYASHI /* In this file, the waitqueues are implemented as circular arrays. */
33*820c1a8dSHiroo HAYASHI #define glwthread_waitqueue_t glwthread_carray_waitqueue_t
34*820c1a8dSHiroo HAYASHI
35*820c1a8dSHiroo HAYASHI static void
glwthread_waitqueue_init(glwthread_waitqueue_t * wq)36*820c1a8dSHiroo HAYASHI glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
37*820c1a8dSHiroo HAYASHI {
38*820c1a8dSHiroo HAYASHI wq->array = NULL;
39*820c1a8dSHiroo HAYASHI wq->count = 0;
40*820c1a8dSHiroo HAYASHI wq->alloc = 0;
41*820c1a8dSHiroo HAYASHI wq->offset = 0;
42*820c1a8dSHiroo HAYASHI }
43*820c1a8dSHiroo HAYASHI
44*820c1a8dSHiroo HAYASHI /* Enqueues the current thread, represented by an event, in a wait queue.
45*820c1a8dSHiroo HAYASHI Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
46*820c1a8dSHiroo HAYASHI static HANDLE
glwthread_waitqueue_add(glwthread_waitqueue_t * wq)47*820c1a8dSHiroo HAYASHI glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
48*820c1a8dSHiroo HAYASHI {
49*820c1a8dSHiroo HAYASHI HANDLE event;
50*820c1a8dSHiroo HAYASHI unsigned int index;
51*820c1a8dSHiroo HAYASHI
52*820c1a8dSHiroo HAYASHI if (wq->count == wq->alloc)
53*820c1a8dSHiroo HAYASHI {
54*820c1a8dSHiroo HAYASHI unsigned int new_alloc = 2 * wq->alloc + 1;
55*820c1a8dSHiroo HAYASHI HANDLE *new_array =
56*820c1a8dSHiroo HAYASHI (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
57*820c1a8dSHiroo HAYASHI if (new_array == NULL)
58*820c1a8dSHiroo HAYASHI /* No more memory. */
59*820c1a8dSHiroo HAYASHI return INVALID_HANDLE_VALUE;
60*820c1a8dSHiroo HAYASHI /* Now is a good opportunity to rotate the array so that its contents
61*820c1a8dSHiroo HAYASHI starts at offset 0. */
62*820c1a8dSHiroo HAYASHI if (wq->offset > 0)
63*820c1a8dSHiroo HAYASHI {
64*820c1a8dSHiroo HAYASHI unsigned int old_count = wq->count;
65*820c1a8dSHiroo HAYASHI unsigned int old_alloc = wq->alloc;
66*820c1a8dSHiroo HAYASHI unsigned int old_offset = wq->offset;
67*820c1a8dSHiroo HAYASHI unsigned int i;
68*820c1a8dSHiroo HAYASHI if (old_offset + old_count > old_alloc)
69*820c1a8dSHiroo HAYASHI {
70*820c1a8dSHiroo HAYASHI unsigned int limit = old_offset + old_count - old_alloc;
71*820c1a8dSHiroo HAYASHI for (i = 0; i < limit; i++)
72*820c1a8dSHiroo HAYASHI new_array[old_alloc + i] = new_array[i];
73*820c1a8dSHiroo HAYASHI }
74*820c1a8dSHiroo HAYASHI for (i = 0; i < old_count; i++)
75*820c1a8dSHiroo HAYASHI new_array[i] = new_array[old_offset + i];
76*820c1a8dSHiroo HAYASHI wq->offset = 0;
77*820c1a8dSHiroo HAYASHI }
78*820c1a8dSHiroo HAYASHI wq->array = new_array;
79*820c1a8dSHiroo HAYASHI wq->alloc = new_alloc;
80*820c1a8dSHiroo HAYASHI }
81*820c1a8dSHiroo HAYASHI /* Whether the created event is a manual-reset one or an auto-reset one,
82*820c1a8dSHiroo HAYASHI does not matter, since we will wait on it only once. */
83*820c1a8dSHiroo HAYASHI event = CreateEvent (NULL, TRUE, FALSE, NULL);
84*820c1a8dSHiroo HAYASHI if (event == INVALID_HANDLE_VALUE)
85*820c1a8dSHiroo HAYASHI /* No way to allocate an event. */
86*820c1a8dSHiroo HAYASHI return INVALID_HANDLE_VALUE;
87*820c1a8dSHiroo HAYASHI index = wq->offset + wq->count;
88*820c1a8dSHiroo HAYASHI if (index >= wq->alloc)
89*820c1a8dSHiroo HAYASHI index -= wq->alloc;
90*820c1a8dSHiroo HAYASHI wq->array[index] = event;
91*820c1a8dSHiroo HAYASHI wq->count++;
92*820c1a8dSHiroo HAYASHI return event;
93*820c1a8dSHiroo HAYASHI }
94*820c1a8dSHiroo HAYASHI
95*820c1a8dSHiroo HAYASHI /* Notifies the first thread from a wait queue and dequeues it. */
96*820c1a8dSHiroo HAYASHI static void
glwthread_waitqueue_notify_first(glwthread_waitqueue_t * wq)97*820c1a8dSHiroo HAYASHI glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
98*820c1a8dSHiroo HAYASHI {
99*820c1a8dSHiroo HAYASHI SetEvent (wq->array[wq->offset + 0]);
100*820c1a8dSHiroo HAYASHI wq->offset++;
101*820c1a8dSHiroo HAYASHI wq->count--;
102*820c1a8dSHiroo HAYASHI if (wq->count == 0 || wq->offset == wq->alloc)
103*820c1a8dSHiroo HAYASHI wq->offset = 0;
104*820c1a8dSHiroo HAYASHI }
105*820c1a8dSHiroo HAYASHI
106*820c1a8dSHiroo HAYASHI /* Notifies all threads from a wait queue and dequeues them all. */
107*820c1a8dSHiroo HAYASHI static void
glwthread_waitqueue_notify_all(glwthread_waitqueue_t * wq)108*820c1a8dSHiroo HAYASHI glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
109*820c1a8dSHiroo HAYASHI {
110*820c1a8dSHiroo HAYASHI unsigned int i;
111*820c1a8dSHiroo HAYASHI
112*820c1a8dSHiroo HAYASHI for (i = 0; i < wq->count; i++)
113*820c1a8dSHiroo HAYASHI {
114*820c1a8dSHiroo HAYASHI unsigned int index = wq->offset + i;
115*820c1a8dSHiroo HAYASHI if (index >= wq->alloc)
116*820c1a8dSHiroo HAYASHI index -= wq->alloc;
117*820c1a8dSHiroo HAYASHI SetEvent (wq->array[index]);
118*820c1a8dSHiroo HAYASHI }
119*820c1a8dSHiroo HAYASHI wq->count = 0;
120*820c1a8dSHiroo HAYASHI wq->offset = 0;
121*820c1a8dSHiroo HAYASHI }
122*820c1a8dSHiroo HAYASHI
123*820c1a8dSHiroo HAYASHI void
glwthread_rwlock_init(glwthread_rwlock_t * lock)124*820c1a8dSHiroo HAYASHI glwthread_rwlock_init (glwthread_rwlock_t *lock)
125*820c1a8dSHiroo HAYASHI {
126*820c1a8dSHiroo HAYASHI InitializeCriticalSection (&lock->lock);
127*820c1a8dSHiroo HAYASHI glwthread_waitqueue_init (&lock->waiting_readers);
128*820c1a8dSHiroo HAYASHI glwthread_waitqueue_init (&lock->waiting_writers);
129*820c1a8dSHiroo HAYASHI lock->runcount = 0;
130*820c1a8dSHiroo HAYASHI lock->guard.done = 1;
131*820c1a8dSHiroo HAYASHI }
132*820c1a8dSHiroo HAYASHI
133*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_rdlock(glwthread_rwlock_t * lock)134*820c1a8dSHiroo HAYASHI glwthread_rwlock_rdlock (glwthread_rwlock_t *lock)
135*820c1a8dSHiroo HAYASHI {
136*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
137*820c1a8dSHiroo HAYASHI {
138*820c1a8dSHiroo HAYASHI if (InterlockedIncrement (&lock->guard.started) == 0)
139*820c1a8dSHiroo HAYASHI /* This thread is the first one to need this lock. Initialize it. */
140*820c1a8dSHiroo HAYASHI glwthread_rwlock_init (lock);
141*820c1a8dSHiroo HAYASHI else
142*820c1a8dSHiroo HAYASHI {
143*820c1a8dSHiroo HAYASHI /* Don't let lock->guard.started grow and wrap around. */
144*820c1a8dSHiroo HAYASHI InterlockedDecrement (&lock->guard.started);
145*820c1a8dSHiroo HAYASHI /* Yield the CPU while waiting for another thread to finish
146*820c1a8dSHiroo HAYASHI initializing this lock. */
147*820c1a8dSHiroo HAYASHI while (!lock->guard.done)
148*820c1a8dSHiroo HAYASHI Sleep (0);
149*820c1a8dSHiroo HAYASHI }
150*820c1a8dSHiroo HAYASHI }
151*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
152*820c1a8dSHiroo HAYASHI /* Test whether only readers are currently running, and whether the runcount
153*820c1a8dSHiroo HAYASHI field will not overflow, and whether no writer is waiting. The latter
154*820c1a8dSHiroo HAYASHI condition is because POSIX recommends that "write locks shall take
155*820c1a8dSHiroo HAYASHI precedence over read locks", to avoid "writer starvation". */
156*820c1a8dSHiroo HAYASHI if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
157*820c1a8dSHiroo HAYASHI {
158*820c1a8dSHiroo HAYASHI /* This thread has to wait for a while. Enqueue it among the
159*820c1a8dSHiroo HAYASHI waiting_readers. */
160*820c1a8dSHiroo HAYASHI HANDLE event = glwthread_waitqueue_add (&lock->waiting_readers);
161*820c1a8dSHiroo HAYASHI if (event != INVALID_HANDLE_VALUE)
162*820c1a8dSHiroo HAYASHI {
163*820c1a8dSHiroo HAYASHI DWORD result;
164*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
165*820c1a8dSHiroo HAYASHI /* Wait until another thread signals this event. */
166*820c1a8dSHiroo HAYASHI result = WaitForSingleObject (event, INFINITE);
167*820c1a8dSHiroo HAYASHI if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
168*820c1a8dSHiroo HAYASHI abort ();
169*820c1a8dSHiroo HAYASHI CloseHandle (event);
170*820c1a8dSHiroo HAYASHI /* The thread which signalled the event already did the bookkeeping:
171*820c1a8dSHiroo HAYASHI removed us from the waiting_readers, incremented lock->runcount. */
172*820c1a8dSHiroo HAYASHI if (!(lock->runcount > 0))
173*820c1a8dSHiroo HAYASHI abort ();
174*820c1a8dSHiroo HAYASHI return 0;
175*820c1a8dSHiroo HAYASHI }
176*820c1a8dSHiroo HAYASHI else
177*820c1a8dSHiroo HAYASHI {
178*820c1a8dSHiroo HAYASHI /* Allocation failure. Weird. */
179*820c1a8dSHiroo HAYASHI do
180*820c1a8dSHiroo HAYASHI {
181*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
182*820c1a8dSHiroo HAYASHI Sleep (1);
183*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
184*820c1a8dSHiroo HAYASHI }
185*820c1a8dSHiroo HAYASHI while (!(lock->runcount + 1 > 0));
186*820c1a8dSHiroo HAYASHI }
187*820c1a8dSHiroo HAYASHI }
188*820c1a8dSHiroo HAYASHI lock->runcount++;
189*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
190*820c1a8dSHiroo HAYASHI return 0;
191*820c1a8dSHiroo HAYASHI }
192*820c1a8dSHiroo HAYASHI
193*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_wrlock(glwthread_rwlock_t * lock)194*820c1a8dSHiroo HAYASHI glwthread_rwlock_wrlock (glwthread_rwlock_t *lock)
195*820c1a8dSHiroo HAYASHI {
196*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
197*820c1a8dSHiroo HAYASHI {
198*820c1a8dSHiroo HAYASHI if (InterlockedIncrement (&lock->guard.started) == 0)
199*820c1a8dSHiroo HAYASHI /* This thread is the first one to need this lock. Initialize it. */
200*820c1a8dSHiroo HAYASHI glwthread_rwlock_init (lock);
201*820c1a8dSHiroo HAYASHI else
202*820c1a8dSHiroo HAYASHI {
203*820c1a8dSHiroo HAYASHI /* Don't let lock->guard.started grow and wrap around. */
204*820c1a8dSHiroo HAYASHI InterlockedDecrement (&lock->guard.started);
205*820c1a8dSHiroo HAYASHI /* Yield the CPU while waiting for another thread to finish
206*820c1a8dSHiroo HAYASHI initializing this lock. */
207*820c1a8dSHiroo HAYASHI while (!lock->guard.done)
208*820c1a8dSHiroo HAYASHI Sleep (0);
209*820c1a8dSHiroo HAYASHI }
210*820c1a8dSHiroo HAYASHI }
211*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
212*820c1a8dSHiroo HAYASHI /* Test whether no readers or writers are currently running. */
213*820c1a8dSHiroo HAYASHI if (!(lock->runcount == 0))
214*820c1a8dSHiroo HAYASHI {
215*820c1a8dSHiroo HAYASHI /* This thread has to wait for a while. Enqueue it among the
216*820c1a8dSHiroo HAYASHI waiting_writers. */
217*820c1a8dSHiroo HAYASHI HANDLE event = glwthread_waitqueue_add (&lock->waiting_writers);
218*820c1a8dSHiroo HAYASHI if (event != INVALID_HANDLE_VALUE)
219*820c1a8dSHiroo HAYASHI {
220*820c1a8dSHiroo HAYASHI DWORD result;
221*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
222*820c1a8dSHiroo HAYASHI /* Wait until another thread signals this event. */
223*820c1a8dSHiroo HAYASHI result = WaitForSingleObject (event, INFINITE);
224*820c1a8dSHiroo HAYASHI if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
225*820c1a8dSHiroo HAYASHI abort ();
226*820c1a8dSHiroo HAYASHI CloseHandle (event);
227*820c1a8dSHiroo HAYASHI /* The thread which signalled the event already did the bookkeeping:
228*820c1a8dSHiroo HAYASHI removed us from the waiting_writers, set lock->runcount = -1. */
229*820c1a8dSHiroo HAYASHI if (!(lock->runcount == -1))
230*820c1a8dSHiroo HAYASHI abort ();
231*820c1a8dSHiroo HAYASHI return 0;
232*820c1a8dSHiroo HAYASHI }
233*820c1a8dSHiroo HAYASHI else
234*820c1a8dSHiroo HAYASHI {
235*820c1a8dSHiroo HAYASHI /* Allocation failure. Weird. */
236*820c1a8dSHiroo HAYASHI do
237*820c1a8dSHiroo HAYASHI {
238*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
239*820c1a8dSHiroo HAYASHI Sleep (1);
240*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
241*820c1a8dSHiroo HAYASHI }
242*820c1a8dSHiroo HAYASHI while (!(lock->runcount == 0));
243*820c1a8dSHiroo HAYASHI }
244*820c1a8dSHiroo HAYASHI }
245*820c1a8dSHiroo HAYASHI lock->runcount--; /* runcount becomes -1 */
246*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
247*820c1a8dSHiroo HAYASHI return 0;
248*820c1a8dSHiroo HAYASHI }
249*820c1a8dSHiroo HAYASHI
250*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_tryrdlock(glwthread_rwlock_t * lock)251*820c1a8dSHiroo HAYASHI glwthread_rwlock_tryrdlock (glwthread_rwlock_t *lock)
252*820c1a8dSHiroo HAYASHI {
253*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
254*820c1a8dSHiroo HAYASHI {
255*820c1a8dSHiroo HAYASHI if (InterlockedIncrement (&lock->guard.started) == 0)
256*820c1a8dSHiroo HAYASHI /* This thread is the first one to need this lock. Initialize it. */
257*820c1a8dSHiroo HAYASHI glwthread_rwlock_init (lock);
258*820c1a8dSHiroo HAYASHI else
259*820c1a8dSHiroo HAYASHI {
260*820c1a8dSHiroo HAYASHI /* Don't let lock->guard.started grow and wrap around. */
261*820c1a8dSHiroo HAYASHI InterlockedDecrement (&lock->guard.started);
262*820c1a8dSHiroo HAYASHI /* Yield the CPU while waiting for another thread to finish
263*820c1a8dSHiroo HAYASHI initializing this lock. */
264*820c1a8dSHiroo HAYASHI while (!lock->guard.done)
265*820c1a8dSHiroo HAYASHI Sleep (0);
266*820c1a8dSHiroo HAYASHI }
267*820c1a8dSHiroo HAYASHI }
268*820c1a8dSHiroo HAYASHI /* It's OK to wait for this critical section, because it is never taken for a
269*820c1a8dSHiroo HAYASHI long time. */
270*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
271*820c1a8dSHiroo HAYASHI /* Test whether only readers are currently running, and whether the runcount
272*820c1a8dSHiroo HAYASHI field will not overflow, and whether no writer is waiting. The latter
273*820c1a8dSHiroo HAYASHI condition is because POSIX recommends that "write locks shall take
274*820c1a8dSHiroo HAYASHI precedence over read locks", to avoid "writer starvation". */
275*820c1a8dSHiroo HAYASHI if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
276*820c1a8dSHiroo HAYASHI {
277*820c1a8dSHiroo HAYASHI /* This thread would have to wait for a while. Return instead. */
278*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
279*820c1a8dSHiroo HAYASHI return EBUSY;
280*820c1a8dSHiroo HAYASHI }
281*820c1a8dSHiroo HAYASHI lock->runcount++;
282*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
283*820c1a8dSHiroo HAYASHI return 0;
284*820c1a8dSHiroo HAYASHI }
285*820c1a8dSHiroo HAYASHI
286*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_trywrlock(glwthread_rwlock_t * lock)287*820c1a8dSHiroo HAYASHI glwthread_rwlock_trywrlock (glwthread_rwlock_t *lock)
288*820c1a8dSHiroo HAYASHI {
289*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
290*820c1a8dSHiroo HAYASHI {
291*820c1a8dSHiroo HAYASHI if (InterlockedIncrement (&lock->guard.started) == 0)
292*820c1a8dSHiroo HAYASHI /* This thread is the first one to need this lock. Initialize it. */
293*820c1a8dSHiroo HAYASHI glwthread_rwlock_init (lock);
294*820c1a8dSHiroo HAYASHI else
295*820c1a8dSHiroo HAYASHI {
296*820c1a8dSHiroo HAYASHI /* Don't let lock->guard.started grow and wrap around. */
297*820c1a8dSHiroo HAYASHI InterlockedDecrement (&lock->guard.started);
298*820c1a8dSHiroo HAYASHI /* Yield the CPU while waiting for another thread to finish
299*820c1a8dSHiroo HAYASHI initializing this lock. */
300*820c1a8dSHiroo HAYASHI while (!lock->guard.done)
301*820c1a8dSHiroo HAYASHI Sleep (0);
302*820c1a8dSHiroo HAYASHI }
303*820c1a8dSHiroo HAYASHI }
304*820c1a8dSHiroo HAYASHI /* It's OK to wait for this critical section, because it is never taken for a
305*820c1a8dSHiroo HAYASHI long time. */
306*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
307*820c1a8dSHiroo HAYASHI /* Test whether no readers or writers are currently running. */
308*820c1a8dSHiroo HAYASHI if (!(lock->runcount == 0))
309*820c1a8dSHiroo HAYASHI {
310*820c1a8dSHiroo HAYASHI /* This thread would have to wait for a while. Return instead. */
311*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
312*820c1a8dSHiroo HAYASHI return EBUSY;
313*820c1a8dSHiroo HAYASHI }
314*820c1a8dSHiroo HAYASHI lock->runcount--; /* runcount becomes -1 */
315*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
316*820c1a8dSHiroo HAYASHI return 0;
317*820c1a8dSHiroo HAYASHI }
318*820c1a8dSHiroo HAYASHI
319*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_unlock(glwthread_rwlock_t * lock)320*820c1a8dSHiroo HAYASHI glwthread_rwlock_unlock (glwthread_rwlock_t *lock)
321*820c1a8dSHiroo HAYASHI {
322*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
323*820c1a8dSHiroo HAYASHI return EINVAL;
324*820c1a8dSHiroo HAYASHI EnterCriticalSection (&lock->lock);
325*820c1a8dSHiroo HAYASHI if (lock->runcount < 0)
326*820c1a8dSHiroo HAYASHI {
327*820c1a8dSHiroo HAYASHI /* Drop a writer lock. */
328*820c1a8dSHiroo HAYASHI if (!(lock->runcount == -1))
329*820c1a8dSHiroo HAYASHI abort ();
330*820c1a8dSHiroo HAYASHI lock->runcount = 0;
331*820c1a8dSHiroo HAYASHI }
332*820c1a8dSHiroo HAYASHI else
333*820c1a8dSHiroo HAYASHI {
334*820c1a8dSHiroo HAYASHI /* Drop a reader lock. */
335*820c1a8dSHiroo HAYASHI if (!(lock->runcount > 0))
336*820c1a8dSHiroo HAYASHI {
337*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
338*820c1a8dSHiroo HAYASHI return EPERM;
339*820c1a8dSHiroo HAYASHI }
340*820c1a8dSHiroo HAYASHI lock->runcount--;
341*820c1a8dSHiroo HAYASHI }
342*820c1a8dSHiroo HAYASHI if (lock->runcount == 0)
343*820c1a8dSHiroo HAYASHI {
344*820c1a8dSHiroo HAYASHI /* POSIX recommends that "write locks shall take precedence over read
345*820c1a8dSHiroo HAYASHI locks", to avoid "writer starvation". */
346*820c1a8dSHiroo HAYASHI if (lock->waiting_writers.count > 0)
347*820c1a8dSHiroo HAYASHI {
348*820c1a8dSHiroo HAYASHI /* Wake up one of the waiting writers. */
349*820c1a8dSHiroo HAYASHI lock->runcount--;
350*820c1a8dSHiroo HAYASHI glwthread_waitqueue_notify_first (&lock->waiting_writers);
351*820c1a8dSHiroo HAYASHI }
352*820c1a8dSHiroo HAYASHI else
353*820c1a8dSHiroo HAYASHI {
354*820c1a8dSHiroo HAYASHI /* Wake up all waiting readers. */
355*820c1a8dSHiroo HAYASHI lock->runcount += lock->waiting_readers.count;
356*820c1a8dSHiroo HAYASHI glwthread_waitqueue_notify_all (&lock->waiting_readers);
357*820c1a8dSHiroo HAYASHI }
358*820c1a8dSHiroo HAYASHI }
359*820c1a8dSHiroo HAYASHI LeaveCriticalSection (&lock->lock);
360*820c1a8dSHiroo HAYASHI return 0;
361*820c1a8dSHiroo HAYASHI }
362*820c1a8dSHiroo HAYASHI
363*820c1a8dSHiroo HAYASHI int
glwthread_rwlock_destroy(glwthread_rwlock_t * lock)364*820c1a8dSHiroo HAYASHI glwthread_rwlock_destroy (glwthread_rwlock_t *lock)
365*820c1a8dSHiroo HAYASHI {
366*820c1a8dSHiroo HAYASHI if (!lock->guard.done)
367*820c1a8dSHiroo HAYASHI return EINVAL;
368*820c1a8dSHiroo HAYASHI if (lock->runcount != 0)
369*820c1a8dSHiroo HAYASHI return EBUSY;
370*820c1a8dSHiroo HAYASHI DeleteCriticalSection (&lock->lock);
371*820c1a8dSHiroo HAYASHI if (lock->waiting_readers.array != NULL)
372*820c1a8dSHiroo HAYASHI free (lock->waiting_readers.array);
373*820c1a8dSHiroo HAYASHI if (lock->waiting_writers.array != NULL)
374*820c1a8dSHiroo HAYASHI free (lock->waiting_writers.array);
375*820c1a8dSHiroo HAYASHI lock->guard.done = 0;
376*820c1a8dSHiroo HAYASHI return 0;
377*820c1a8dSHiroo HAYASHI }
378