xref: /Universal-ctags/gnulib/windows-rwlock.c (revision 820c1a8d46849a90376d8eb15b319ac05439f656)
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