summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2019-07-15 02:36:02 +0200
committerBruno Haible <bruno@clisp.org>2019-07-15 02:36:02 +0200
commit72f60af8edb6e1cdee4e5bb34dbc068113b9479c (patch)
treea6a85f96748079491b13e2a413c356407f124136
parent6f520fb1dd6fcc2caeb2fc9fed9140f5d96f84a7 (diff)
downloadgnulib-72f60af8edb6e1cdee4e5bb34dbc068113b9479c.tar.gz
windows-rwlock: New module.
* lib/windows-rwlock.h: New file, extracted from lib/glthread/lock.h. * lib/windows-rwlock.c: New file, extracted from lib/glthread/lock.c. * lib/glthread/lock.h: Include windows-rwlock.h. Don't include windows-initguard.h. (gl_rwlock_t): Define using glwthread_rwlock_t. (gl_rwlock_initializer): Define using GLWTHREAD_RWLOCK_INIT. (glthread_rwlock_init): Define using glwthread_rwlock_init. (glthread_rwlock_rdlock): Define using glwthread_rwlock_rdlock. (glthread_rwlock_wrlock): Define using glwthread_rwlock_wrlock. (glthread_rwlock_unlock): Define using glwthread_rwlock_unlock. (glthread_rwlock_destroy): Define using glwthread_rwlock_destroy. (glthread_rwlock_init_func, glthread_rwlock_rdlock_func, glthread_rwlock_wrlock_func, glthread_rwlock_unlock_func, glthread_rwlock_destroy_func): Remove declarations. * lib/glthread/lock.c (gl_waitqueue_t): Remove type. (gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_notify_first, gl_waitqueue_notify_all, glthread_rwlock_init_func, glthread_rwlock_rdlock_func, glthread_rwlock_wrlock_func, glthread_rwlock_unlock_func, glthread_rwlock_destroy_func): Remove functions. * modules/windows-rwlock: New file. * modules/lock (Depends-on): Add windows-rwlock.
-rw-r--r--ChangeLog26
-rw-r--r--lib/glthread/lock.c282
-rw-r--r--lib/glthread/lock.h41
-rw-r--r--lib/windows-rwlock.c373
-rw-r--r--lib/windows-rwlock.h68
-rw-r--r--modules/lock1
-rw-r--r--modules/windows-rwlock28
7 files changed, 505 insertions, 314 deletions
diff --git a/ChangeLog b/ChangeLog
index 316ae110e5..3a0d6f1189 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,31 @@
2019-07-14 Bruno Haible <bruno@clisp.org>
+ windows-rwlock: New module.
+ * lib/windows-rwlock.h: New file, extracted from lib/glthread/lock.h.
+ * lib/windows-rwlock.c: New file, extracted from lib/glthread/lock.c.
+ * lib/glthread/lock.h: Include windows-rwlock.h. Don't include
+ windows-initguard.h.
+ (gl_rwlock_t): Define using glwthread_rwlock_t.
+ (gl_rwlock_initializer): Define using GLWTHREAD_RWLOCK_INIT.
+ (glthread_rwlock_init): Define using glwthread_rwlock_init.
+ (glthread_rwlock_rdlock): Define using glwthread_rwlock_rdlock.
+ (glthread_rwlock_wrlock): Define using glwthread_rwlock_wrlock.
+ (glthread_rwlock_unlock): Define using glwthread_rwlock_unlock.
+ (glthread_rwlock_destroy): Define using glwthread_rwlock_destroy.
+ (glthread_rwlock_init_func, glthread_rwlock_rdlock_func,
+ glthread_rwlock_wrlock_func, glthread_rwlock_unlock_func,
+ glthread_rwlock_destroy_func): Remove declarations.
+ * lib/glthread/lock.c (gl_waitqueue_t): Remove type.
+ (gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_notify_first,
+ gl_waitqueue_notify_all, glthread_rwlock_init_func,
+ glthread_rwlock_rdlock_func, glthread_rwlock_wrlock_func,
+ glthread_rwlock_unlock_func, glthread_rwlock_destroy_func): Remove
+ functions.
+ * modules/windows-rwlock: New file.
+ * modules/lock (Depends-on): Add windows-rwlock.
+
+2019-07-14 Bruno Haible <bruno@clisp.org>
+
windows-thread: Add support for creating a thread in detached state.
* lib/windows-thread.h (GLWTHREAD_ATTR_DETACHED): New macro.
(glwthread_thread_create): Add attr argument.
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index 89f1f50ab1..852d84d3bb 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -15,7 +15,7 @@
along with this program; if not, see <https://www.gnu.org/licenses/>. */
/* Written by Bruno Haible <bruno@clisp.org>, 2005.
- Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */
+ Based on GCC's gthr-posix.h, gthr-posix95.h. */
#include <config.h>
@@ -501,286 +501,6 @@ glthread_once_singlethreaded (pthread_once_t *once_control)
#if USE_WINDOWS_THREADS
-/* ------------------------- gl_rwlock_t datatype ------------------------- */
-
-/* In this file, the waitqueues are implemented as circular arrays. */
-#define gl_waitqueue_t gl_carray_waitqueue_t
-
-static void
-gl_waitqueue_init (gl_waitqueue_t *wq)
-{
- wq->array = NULL;
- wq->count = 0;
- wq->alloc = 0;
- wq->offset = 0;
-}
-
-/* Enqueues the current thread, represented by an event, in a wait queue.
- Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
-static HANDLE
-gl_waitqueue_add (gl_waitqueue_t *wq)
-{
- HANDLE event;
- unsigned int index;
-
- if (wq->count == wq->alloc)
- {
- unsigned int new_alloc = 2 * wq->alloc + 1;
- HANDLE *new_array =
- (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
- if (new_array == NULL)
- /* No more memory. */
- return INVALID_HANDLE_VALUE;
- /* Now is a good opportunity to rotate the array so that its contents
- starts at offset 0. */
- if (wq->offset > 0)
- {
- unsigned int old_count = wq->count;
- unsigned int old_alloc = wq->alloc;
- unsigned int old_offset = wq->offset;
- unsigned int i;
- if (old_offset + old_count > old_alloc)
- {
- unsigned int limit = old_offset + old_count - old_alloc;
- for (i = 0; i < limit; i++)
- new_array[old_alloc + i] = new_array[i];
- }
- for (i = 0; i < old_count; i++)
- new_array[i] = new_array[old_offset + i];
- wq->offset = 0;
- }
- wq->array = new_array;
- wq->alloc = new_alloc;
- }
- /* Whether the created event is a manual-reset one or an auto-reset one,
- does not matter, since we will wait on it only once. */
- event = CreateEvent (NULL, TRUE, FALSE, NULL);
- if (event == INVALID_HANDLE_VALUE)
- /* No way to allocate an event. */
- return INVALID_HANDLE_VALUE;
- index = wq->offset + wq->count;
- if (index >= wq->alloc)
- index -= wq->alloc;
- wq->array[index] = event;
- wq->count++;
- return event;
-}
-
-/* Notifies the first thread from a wait queue and dequeues it. */
-static void
-gl_waitqueue_notify_first (gl_waitqueue_t *wq)
-{
- SetEvent (wq->array[wq->offset + 0]);
- wq->offset++;
- wq->count--;
- if (wq->count == 0 || wq->offset == wq->alloc)
- wq->offset = 0;
-}
-
-/* Notifies all threads from a wait queue and dequeues them all. */
-static void
-gl_waitqueue_notify_all (gl_waitqueue_t *wq)
-{
- unsigned int i;
-
- for (i = 0; i < wq->count; i++)
- {
- unsigned int index = wq->offset + i;
- if (index >= wq->alloc)
- index -= wq->alloc;
- SetEvent (wq->array[index]);
- }
- wq->count = 0;
- wq->offset = 0;
-}
-
-void
-glthread_rwlock_init_func (gl_rwlock_t *lock)
-{
- InitializeCriticalSection (&lock->lock);
- gl_waitqueue_init (&lock->waiting_readers);
- gl_waitqueue_init (&lock->waiting_writers);
- lock->runcount = 0;
- lock->guard.done = 1;
-}
-
-int
-glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
-{
- if (!lock->guard.done)
- {
- if (InterlockedIncrement (&lock->guard.started) == 0)
- /* This thread is the first one to need this lock. Initialize it. */
- glthread_rwlock_init (lock);
- else
- {
- /* Don't let lock->guard.started grow and wrap around. */
- InterlockedDecrement (&lock->guard.started);
- /* Yield the CPU while waiting for another thread to finish
- initializing this lock. */
- while (!lock->guard.done)
- Sleep (0);
- }
- }
- EnterCriticalSection (&lock->lock);
- /* Test whether only readers are currently running, and whether the runcount
- field will not overflow, and whether no writer is waiting. The latter
- condition is because POSIX recommends that "write locks shall take
- precedence over read locks", to avoid "writer starvation". */
- if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
- {
- /* This thread has to wait for a while. Enqueue it among the
- waiting_readers. */
- HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
- if (event != INVALID_HANDLE_VALUE)
- {
- DWORD result;
- LeaveCriticalSection (&lock->lock);
- /* Wait until another thread signals this event. */
- result = WaitForSingleObject (event, INFINITE);
- if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
- abort ();
- CloseHandle (event);
- /* The thread which signalled the event already did the bookkeeping:
- removed us from the waiting_readers, incremented lock->runcount. */
- if (!(lock->runcount > 0))
- abort ();
- return 0;
- }
- else
- {
- /* Allocation failure. Weird. */
- do
- {
- LeaveCriticalSection (&lock->lock);
- Sleep (1);
- EnterCriticalSection (&lock->lock);
- }
- while (!(lock->runcount + 1 > 0));
- }
- }
- lock->runcount++;
- LeaveCriticalSection (&lock->lock);
- return 0;
-}
-
-int
-glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
-{
- if (!lock->guard.done)
- {
- if (InterlockedIncrement (&lock->guard.started) == 0)
- /* This thread is the first one to need this lock. Initialize it. */
- glthread_rwlock_init (lock);
- else
- {
- /* Don't let lock->guard.started grow and wrap around. */
- InterlockedDecrement (&lock->guard.started);
- /* Yield the CPU while waiting for another thread to finish
- initializing this lock. */
- while (!lock->guard.done)
- Sleep (0);
- }
- }
- EnterCriticalSection (&lock->lock);
- /* Test whether no readers or writers are currently running. */
- if (!(lock->runcount == 0))
- {
- /* This thread has to wait for a while. Enqueue it among the
- waiting_writers. */
- HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
- if (event != INVALID_HANDLE_VALUE)
- {
- DWORD result;
- LeaveCriticalSection (&lock->lock);
- /* Wait until another thread signals this event. */
- result = WaitForSingleObject (event, INFINITE);
- if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
- abort ();
- CloseHandle (event);
- /* The thread which signalled the event already did the bookkeeping:
- removed us from the waiting_writers, set lock->runcount = -1. */
- if (!(lock->runcount == -1))
- abort ();
- return 0;
- }
- else
- {
- /* Allocation failure. Weird. */
- do
- {
- LeaveCriticalSection (&lock->lock);
- Sleep (1);
- EnterCriticalSection (&lock->lock);
- }
- while (!(lock->runcount == 0));
- }
- }
- lock->runcount--; /* runcount becomes -1 */
- LeaveCriticalSection (&lock->lock);
- return 0;
-}
-
-int
-glthread_rwlock_unlock_func (gl_rwlock_t *lock)
-{
- if (!lock->guard.done)
- return EINVAL;
- EnterCriticalSection (&lock->lock);
- if (lock->runcount < 0)
- {
- /* Drop a writer lock. */
- if (!(lock->runcount == -1))
- abort ();
- lock->runcount = 0;
- }
- else
- {
- /* Drop a reader lock. */
- if (!(lock->runcount > 0))
- {
- LeaveCriticalSection (&lock->lock);
- return EPERM;
- }
- lock->runcount--;
- }
- if (lock->runcount == 0)
- {
- /* POSIX recommends that "write locks shall take precedence over read
- locks", to avoid "writer starvation". */
- if (lock->waiting_writers.count > 0)
- {
- /* Wake up one of the waiting writers. */
- lock->runcount--;
- gl_waitqueue_notify_first (&lock->waiting_writers);
- }
- else
- {
- /* Wake up all waiting readers. */
- lock->runcount += lock->waiting_readers.count;
- gl_waitqueue_notify_all (&lock->waiting_readers);
- }
- }
- LeaveCriticalSection (&lock->lock);
- return 0;
-}
-
-int
-glthread_rwlock_destroy_func (gl_rwlock_t *lock)
-{
- if (!lock->guard.done)
- return EINVAL;
- if (lock->runcount != 0)
- return EBUSY;
- DeleteCriticalSection (&lock->lock);
- if (lock->waiting_readers.array != NULL)
- free (lock->waiting_readers.array);
- if (lock->waiting_writers.array != NULL)
- free (lock->waiting_writers.array);
- lock->guard.done = 0;
- return 0;
-}
-
#endif
/* ========================================================================= */
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 23d2d98506..78dd67f29f 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -410,8 +410,8 @@ extern int glthread_once_singlethreaded (pthread_once_t *once_control);
# define WIN32_LEAN_AND_MEAN /* avoid including junk */
# include <windows.h>
-# include "windows-initguard.h"
# include "windows-mutex.h"
+# include "windows-rwlock.h"
# include "windows-recmutex.h"
# include "windows-once.h"
@@ -450,48 +450,23 @@ typedef glwthread_mutex_t gl_lock_t;
/* ------------------------- gl_rwlock_t datatype ------------------------- */
-/* It is impossible to implement read-write locks using plain locks, without
- introducing an extra thread dedicated to managing read-write locks.
- Therefore here we need to use the low-level Event type. */
-
-typedef struct
- {
- HANDLE *array; /* array of waiting threads, each represented by an event */
- unsigned int count; /* number of waiting threads */
- unsigned int alloc; /* length of allocated array */
- unsigned int offset; /* index of first waiting thread in array */
- }
- gl_carray_waitqueue_t;
-typedef struct
- {
- glwthread_initguard_t guard; /* protects the initialization */
- CRITICAL_SECTION lock; /* protects the remaining fields */
- gl_carray_waitqueue_t waiting_readers; /* waiting readers */
- gl_carray_waitqueue_t waiting_writers; /* waiting writers */
- int runcount; /* number of readers running, or -1 when a writer runs */
- }
- gl_rwlock_t;
+typedef glwthread_rwlock_t gl_rwlock_t;
# define gl_rwlock_define(STORAGECLASS, NAME) \
STORAGECLASS gl_rwlock_t NAME;
# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer;
# define gl_rwlock_initializer \
- { GLWTHREAD_INITGUARD_INIT }
+ GLWTHREAD_RWLOCK_INIT
# define glthread_rwlock_init(LOCK) \
- (glthread_rwlock_init_func (LOCK), 0)
+ (glwthread_rwlock_init (LOCK), 0)
# define glthread_rwlock_rdlock(LOCK) \
- glthread_rwlock_rdlock_func (LOCK)
+ glwthread_rwlock_rdlock (LOCK)
# define glthread_rwlock_wrlock(LOCK) \
- glthread_rwlock_wrlock_func (LOCK)
+ glwthread_rwlock_wrlock (LOCK)
# define glthread_rwlock_unlock(LOCK) \
- glthread_rwlock_unlock_func (LOCK)
+ glwthread_rwlock_unlock (LOCK)
# define glthread_rwlock_destroy(LOCK) \
- glthread_rwlock_destroy_func (LOCK)
-extern void glthread_rwlock_init_func (gl_rwlock_t *lock);
-extern int glthread_rwlock_rdlock_func (gl_rwlock_t *lock);
-extern int glthread_rwlock_wrlock_func (gl_rwlock_t *lock);
-extern int glthread_rwlock_unlock_func (gl_rwlock_t *lock);
-extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock);
+ glwthread_rwlock_destroy (LOCK)
/* --------------------- gl_recursive_lock_t datatype --------------------- */
diff --git a/lib/windows-rwlock.c b/lib/windows-rwlock.c
new file mode 100644
index 0000000000..d777307b09
--- /dev/null
+++ b/lib/windows-rwlock.c
@@ -0,0 +1,373 @@
+/* Read-write locks (native Windows implementation).
+ Copyright (C) 2005-2019 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+ Based on GCC's gthr-win32.h. */
+
+#include <config.h>
+
+/* Specification. */
+#include "windows-rwlock.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+/* In this file, the waitqueues are implemented as circular arrays. */
+#define glwthread_waitqueue_t glwthread_carray_waitqueue_t
+
+static void
+glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
+{
+ wq->array = NULL;
+ wq->count = 0;
+ wq->alloc = 0;
+ wq->offset = 0;
+}
+
+/* Enqueues the current thread, represented by an event, in a wait queue.
+ Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
+static HANDLE
+glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
+{
+ HANDLE event;
+ unsigned int index;
+
+ if (wq->count == wq->alloc)
+ {
+ unsigned int new_alloc = 2 * wq->alloc + 1;
+ HANDLE *new_array =
+ (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
+ if (new_array == NULL)
+ /* No more memory. */
+ return INVALID_HANDLE_VALUE;
+ /* Now is a good opportunity to rotate the array so that its contents
+ starts at offset 0. */
+ if (wq->offset > 0)
+ {
+ unsigned int old_count = wq->count;
+ unsigned int old_alloc = wq->alloc;
+ unsigned int old_offset = wq->offset;
+ unsigned int i;
+ if (old_offset + old_count > old_alloc)
+ {
+ unsigned int limit = old_offset + old_count - old_alloc;
+ for (i = 0; i < limit; i++)
+ new_array[old_alloc + i] = new_array[i];
+ }
+ for (i = 0; i < old_count; i++)
+ new_array[i] = new_array[old_offset + i];
+ wq->offset = 0;
+ }
+ wq->array = new_array;
+ wq->alloc = new_alloc;
+ }
+ /* Whether the created event is a manual-reset one or an auto-reset one,
+ does not matter, since we will wait on it only once. */
+ event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (event == INVALID_HANDLE_VALUE)
+ /* No way to allocate an event. */
+ return INVALID_HANDLE_VALUE;
+ index = wq->offset + wq->count;
+ if (index >= wq->alloc)
+ index -= wq->alloc;
+ wq->array[index] = event;
+ wq->count++;
+ return event;
+}
+
+/* Notifies the first thread from a wait queue and dequeues it. */
+static void
+glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
+{
+ SetEvent (wq->array[wq->offset + 0]);
+ wq->offset++;
+ wq->count--;
+ if (wq->count == 0 || wq->offset == wq->alloc)
+ wq->offset = 0;
+}
+
+/* Notifies all threads from a wait queue and dequeues them all. */
+static void
+glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
+{
+ unsigned int i;
+
+ for (i = 0; i < wq->count; i++)
+ {
+ unsigned int index = wq->offset + i;
+ if (index >= wq->alloc)
+ index -= wq->alloc;
+ SetEvent (wq->array[index]);
+ }
+ wq->count = 0;
+ wq->offset = 0;
+}
+
+void
+glwthread_rwlock_init (glwthread_rwlock_t *lock)
+{
+ InitializeCriticalSection (&lock->lock);
+ glwthread_waitqueue_init (&lock->waiting_readers);
+ glwthread_waitqueue_init (&lock->waiting_writers);
+ lock->runcount = 0;
+ lock->guard.done = 1;
+}
+
+int
+glwthread_rwlock_rdlock (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ {
+ if (InterlockedIncrement (&lock->guard.started) == 0)
+ /* This thread is the first one to need this lock. Initialize it. */
+ glwthread_rwlock_init (lock);
+ else
+ {
+ /* Don't let lock->guard.started grow and wrap around. */
+ InterlockedDecrement (&lock->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this lock. */
+ while (!lock->guard.done)
+ Sleep (0);
+ }
+ }
+ EnterCriticalSection (&lock->lock);
+ /* Test whether only readers are currently running, and whether the runcount
+ field will not overflow, and whether no writer is waiting. The latter
+ condition is because POSIX recommends that "write locks shall take
+ precedence over read locks", to avoid "writer starvation". */
+ if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
+ {
+ /* This thread has to wait for a while. Enqueue it among the
+ waiting_readers. */
+ HANDLE event = glwthread_waitqueue_add (&lock->waiting_readers);
+ if (event != INVALID_HANDLE_VALUE)
+ {
+ DWORD result;
+ LeaveCriticalSection (&lock->lock);
+ /* Wait until another thread signals this event. */
+ result = WaitForSingleObject (event, INFINITE);
+ if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+ abort ();
+ CloseHandle (event);
+ /* The thread which signalled the event already did the bookkeeping:
+ removed us from the waiting_readers, incremented lock->runcount. */
+ if (!(lock->runcount > 0))
+ abort ();
+ return 0;
+ }
+ else
+ {
+ /* Allocation failure. Weird. */
+ do
+ {
+ LeaveCriticalSection (&lock->lock);
+ Sleep (1);
+ EnterCriticalSection (&lock->lock);
+ }
+ while (!(lock->runcount + 1 > 0));
+ }
+ }
+ lock->runcount++;
+ LeaveCriticalSection (&lock->lock);
+ return 0;
+}
+
+int
+glwthread_rwlock_wrlock (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ {
+ if (InterlockedIncrement (&lock->guard.started) == 0)
+ /* This thread is the first one to need this lock. Initialize it. */
+ glwthread_rwlock_init (lock);
+ else
+ {
+ /* Don't let lock->guard.started grow and wrap around. */
+ InterlockedDecrement (&lock->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this lock. */
+ while (!lock->guard.done)
+ Sleep (0);
+ }
+ }
+ EnterCriticalSection (&lock->lock);
+ /* Test whether no readers or writers are currently running. */
+ if (!(lock->runcount == 0))
+ {
+ /* This thread has to wait for a while. Enqueue it among the
+ waiting_writers. */
+ HANDLE event = glwthread_waitqueue_add (&lock->waiting_writers);
+ if (event != INVALID_HANDLE_VALUE)
+ {
+ DWORD result;
+ LeaveCriticalSection (&lock->lock);
+ /* Wait until another thread signals this event. */
+ result = WaitForSingleObject (event, INFINITE);
+ if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+ abort ();
+ CloseHandle (event);
+ /* The thread which signalled the event already did the bookkeeping:
+ removed us from the waiting_writers, set lock->runcount = -1. */
+ if (!(lock->runcount == -1))
+ abort ();
+ return 0;
+ }
+ else
+ {
+ /* Allocation failure. Weird. */
+ do
+ {
+ LeaveCriticalSection (&lock->lock);
+ Sleep (1);
+ EnterCriticalSection (&lock->lock);
+ }
+ while (!(lock->runcount == 0));
+ }
+ }
+ lock->runcount--; /* runcount becomes -1 */
+ LeaveCriticalSection (&lock->lock);
+ return 0;
+}
+
+int
+glwthread_rwlock_tryrdlock (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ {
+ if (InterlockedIncrement (&lock->guard.started) == 0)
+ /* This thread is the first one to need this lock. Initialize it. */
+ glwthread_rwlock_init (lock);
+ else
+ {
+ /* Don't let lock->guard.started grow and wrap around. */
+ InterlockedDecrement (&lock->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this lock. */
+ while (!lock->guard.done)
+ Sleep (0);
+ }
+ }
+ /* It's OK to wait for this critical section, because it is never taken for a
+ long time. */
+ EnterCriticalSection (&lock->lock);
+ /* Test whether only readers are currently running, and whether the runcount
+ field will not overflow, and whether no writer is waiting. The latter
+ condition is because POSIX recommends that "write locks shall take
+ precedence over read locks", to avoid "writer starvation". */
+ if (!(lock->runcount + 1 > 0 && lock->waiting_writers.count == 0))
+ {
+ /* This thread would have to wait for a while. Return instead. */
+ LeaveCriticalSection (&lock->lock);
+ return EBUSY;
+ }
+ lock->runcount++;
+ LeaveCriticalSection (&lock->lock);
+ return 0;
+}
+
+int
+glwthread_rwlock_trywrlock (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ {
+ if (InterlockedIncrement (&lock->guard.started) == 0)
+ /* This thread is the first one to need this lock. Initialize it. */
+ glwthread_rwlock_init (lock);
+ else
+ {
+ /* Don't let lock->guard.started grow and wrap around. */
+ InterlockedDecrement (&lock->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this lock. */
+ while (!lock->guard.done)
+ Sleep (0);
+ }
+ }
+ /* It's OK to wait for this critical section, because it is never taken for a
+ long time. */
+ EnterCriticalSection (&lock->lock);
+ /* Test whether no readers or writers are currently running. */
+ if (!(lock->runcount == 0))
+ {
+ /* This thread would have to wait for a while. Return instead. */
+ LeaveCriticalSection (&lock->lock);
+ return EBUSY;
+ }
+ lock->runcount--; /* runcount becomes -1 */
+ LeaveCriticalSection (&lock->lock);
+ return 0;
+}
+
+int
+glwthread_rwlock_unlock (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ return EINVAL;
+ EnterCriticalSection (&lock->lock);
+ if (lock->runcount < 0)
+ {
+ /* Drop a writer lock. */
+ if (!(lock->runcount == -1))
+ abort ();
+ lock->runcount = 0;
+ }
+ else
+ {
+ /* Drop a reader lock. */
+ if (!(lock->runcount > 0))
+ {
+ LeaveCriticalSection (&lock->lock);
+ return EPERM;
+ }
+ lock->runcount--;
+ }
+ if (lock->runcount == 0)
+ {
+ /* POSIX recommends that "write locks shall take precedence over read
+ locks", to avoid "writer starvation". */
+ if (lock->waiting_writers.count > 0)
+ {
+ /* Wake up one of the waiting writers. */
+ lock->runcount--;
+ glwthread_waitqueue_notify_first (&lock->waiting_writers);
+ }
+ else
+ {
+ /* Wake up all waiting readers. */
+ lock->runcount += lock->waiting_readers.count;
+ glwthread_waitqueue_notify_all (&lock->waiting_readers);
+ }
+ }
+ LeaveCriticalSection (&lock->lock);
+ return 0;
+}
+
+int
+glwthread_rwlock_destroy (glwthread_rwlock_t *lock)
+{
+ if (!lock->guard.done)
+ return EINVAL;
+ if (lock->runcount != 0)
+ return EBUSY;
+ DeleteCriticalSection (&lock->lock);
+ if (lock->waiting_readers.array != NULL)
+ free (lock->waiting_readers.array);
+ if (lock->waiting_writers.array != NULL)
+ free (lock->waiting_writers.array);
+ lock->guard.done = 0;
+ return 0;
+}
diff --git a/lib/windows-rwlock.h b/lib/windows-rwlock.h
new file mode 100644
index 0000000000..ff4272a589
--- /dev/null
+++ b/lib/windows-rwlock.h
@@ -0,0 +1,68 @@
+/* Read-write locks (native Windows implementation).
+ Copyright (C) 2005-2019 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+ Based on GCC's gthr-win32.h. */
+
+#ifndef _WINDOWS_RWLOCK_H
+#define _WINDOWS_RWLOCK_H
+
+#define WIN32_LEAN_AND_MEAN /* avoid including junk */
+#include <windows.h>
+
+#include "windows-initguard.h"
+
+/* It is impossible to implement read-write locks using plain locks, without
+ introducing an extra thread dedicated to managing read-write locks.
+ Therefore here we need to use the low-level Event type. */
+
+typedef struct
+ {
+ HANDLE *array; /* array of waiting threads, each represented by an event */
+ unsigned int count; /* number of waiting threads */
+ unsigned int alloc; /* length of allocated array */
+ unsigned int offset; /* index of first waiting thread in array */
+ }
+ glwthread_carray_waitqueue_t;
+typedef struct
+ {
+ glwthread_initguard_t guard; /* protects the initialization */
+ CRITICAL_SECTION lock; /* protects the remaining fields */
+ glwthread_carray_waitqueue_t waiting_readers; /* waiting readers */
+ glwthread_carray_waitqueue_t waiting_writers; /* waiting writers */
+ int runcount; /* number of readers running, or -1 when a writer runs */
+ }
+ glwthread_rwlock_t;
+
+#define GLWTHREAD_RWLOCK_INIT { GLWTHREAD_INITGUARD_INIT }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void glwthread_rwlock_init (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_rdlock (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_wrlock (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_tryrdlock (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_trywrlock (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_unlock (glwthread_rwlock_t *lock);
+extern int glwthread_rwlock_destroy (glwthread_rwlock_t *lock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_RWLOCK_H */
diff --git a/modules/lock b/modules/lock
index 0be823c76b..4b390ba765 100644
--- a/modules/lock
+++ b/modules/lock
@@ -11,6 +11,7 @@ Depends-on:
extensions
threadlib
windows-mutex [test $gl_threads_api = windows]
+windows-rwlock [test $gl_threads_api = windows]
windows-recmutex [test $gl_threads_api = windows]
windows-once [test $gl_threads_api = windows]
diff --git a/modules/windows-rwlock b/modules/windows-rwlock
new file mode 100644
index 0000000000..665d60245e
--- /dev/null
+++ b/modules/windows-rwlock
@@ -0,0 +1,28 @@
+Description:
+Read-write locks (native Windows implementation).
+
+Files:
+lib/windows-rwlock.h
+lib/windows-rwlock.c
+lib/windows-initguard.h
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+ mingw*)
+ AC_LIBOBJ([windows-rwlock])
+ ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-rwlock.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all