>From 7b7305f41af7c79ab3739704febba7cf171b404a Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Thu, 20 Jun 2019 04:13:43 +0200
Subject: [PATCH 13/26] windows-cond: New module.
* lib/windows-cond.h: New file, based on lib/glthread/cond.h.
* lib/windows-cond.c: New file, based on lib/glthread/cond.c.
* lib/glthread/cond.h: Include windows-cond.h.
(struct gl_waitqueue_link, gl_linked_waitqueue_t): Remove types.
(gl_cond_t): Define using glwthread_cond_t.
(gl_cond_initializer): Define using GLWTHREAD_COND_INIT.
(glthread_cond_init): Define using glwthread_cond_init.
(glthread_cond_wait): Define using glwthread_cond_wait.
(glthread_cond_timedwait): Define using glwthread_cond_timedwait.
(glthread_cond_signal): Define using glwthread_cond_signal.
(glthread_cond_broadcast): Define using glwthread_cond_broadcast.
(glthread_cond_destroy): Define using glwthread_cond_destroy.
(glthread_cond_init_func, glthread_cond_wait_func,
glthread_cond_timedwait_func, glthread_cond_signal_func,
glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
declarations.
* lib/glthread/cond.c (gl_waitqueue_t, gl_waitqueue_element): Remove
types.
(gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove,
gl_waitqueue_notify_first, gl_waitqueue_notify_all,
glthread_cond_init_func, glthread_cond_wait_func,
glthread_cond_timedwait_func, glthread_cond_signal_func,
glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
functions.
* modules/windows-cond: New file.
* modules/cond (Depends-on): Add windows-cond. Remove gettimeofday.
---
ChangeLog | 30 ++++
lib/glthread/cond.c | 394 -----------------------------------------------
lib/glthread/cond.h | 45 ++----
lib/windows-cond.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/windows-cond.h | 74 +++++++++
modules/cond | 2 +-
modules/windows-cond | 32 ++++
7 files changed, 577 insertions(+), 425 deletions(-)
create mode 100644 lib/windows-cond.c
create mode 100644 lib/windows-cond.h
create mode 100644 modules/windows-cond
diff --git a/ChangeLog b/ChangeLog
index 5e416fe..6307fcc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,35 @@
2019-06-20 Bruno Haible
+ windows-cond: New module.
+ * lib/windows-cond.h: New file, based on lib/glthread/cond.h.
+ * lib/windows-cond.c: New file, based on lib/glthread/cond.c.
+ * lib/glthread/cond.h: Include windows-cond.h.
+ (struct gl_waitqueue_link, gl_linked_waitqueue_t): Remove types.
+ (gl_cond_t): Define using glwthread_cond_t.
+ (gl_cond_initializer): Define using GLWTHREAD_COND_INIT.
+ (glthread_cond_init): Define using glwthread_cond_init.
+ (glthread_cond_wait): Define using glwthread_cond_wait.
+ (glthread_cond_timedwait): Define using glwthread_cond_timedwait.
+ (glthread_cond_signal): Define using glwthread_cond_signal.
+ (glthread_cond_broadcast): Define using glwthread_cond_broadcast.
+ (glthread_cond_destroy): Define using glwthread_cond_destroy.
+ (glthread_cond_init_func, glthread_cond_wait_func,
+ glthread_cond_timedwait_func, glthread_cond_signal_func,
+ glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
+ declarations.
+ * lib/glthread/cond.c (gl_waitqueue_t, gl_waitqueue_element): Remove
+ types.
+ (gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove,
+ gl_waitqueue_notify_first, gl_waitqueue_notify_all,
+ glthread_cond_init_func, glthread_cond_wait_func,
+ glthread_cond_timedwait_func, glthread_cond_signal_func,
+ glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
+ functions.
+ * modules/windows-cond: New file.
+ * modules/cond (Depends-on): Add windows-cond. Remove gettimeofday.
+
+2019-06-20 Bruno Haible
+
windows-timedrecmutex: New module.
* lib/windows-timedrecmutex.h: New file, based on windows-recmutex.h.
* lib/windows-timedrecmutex.c: New file, based on windows-recmutex.c.
diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c
index 6df6780..2285c16 100644
--- a/lib/glthread/cond.c
+++ b/lib/glthread/cond.c
@@ -75,400 +75,6 @@ glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
#if USE_WINDOWS_THREADS
-#include
-
-/* -------------------------- gl_cond_t datatype -------------------------- */
-
-/* In this file, the waitqueues are implemented as linked lists. */
-#define gl_waitqueue_t gl_linked_waitqueue_t
-
-/* All links of a circular list, except the anchor, are of this type, carrying
- a payload. */
-struct gl_waitqueue_element
-{
- struct gl_waitqueue_link link; /* must be the first field! */
- HANDLE event; /* Waiting thread, represented by an event.
- This field is immutable once initialized. */
-};
-
-static void
-gl_waitqueue_init (gl_waitqueue_t *wq)
-{
- wq->wq_list.wql_next = &wq->wq_list;
- wq->wq_list.wql_prev = &wq->wq_list;
-}
-
-/* Enqueues the current thread, represented by an event, in a wait queue.
- Returns NULL if an allocation failure occurs. */
-static struct gl_waitqueue_element *
-gl_waitqueue_add (gl_waitqueue_t *wq)
-{
- struct gl_waitqueue_element *elt;
- HANDLE event;
-
- /* Allocate the memory for the waitqueue element on the heap, not on the
- thread's stack. If the thread exits unexpectedly, we prefer to leak
- some memory rather than to access unavailable memory and crash. */
- elt =
- (struct gl_waitqueue_element *)
- malloc (sizeof (struct gl_waitqueue_element));
- if (elt == NULL)
- /* No more memory. */
- return NULL;
-
- /* 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. */
- free (elt);
- return NULL;
- }
- elt->event = event;
- /* Insert elt at the end of the circular list. */
- (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
- (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
- return elt;
-}
-
-/* Removes the current thread, represented by a 'struct gl_waitqueue_element *',
- from a wait queue.
- Returns true if is was found and removed, false if it was not present. */
-static bool
-gl_waitqueue_remove (gl_waitqueue_t *wq, struct gl_waitqueue_element *elt)
-{
- if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
- {
- /* Remove elt from the circular list. */
- struct gl_waitqueue_link *prev = elt->link.wql_prev;
- struct gl_waitqueue_link *next = elt->link.wql_next;
- prev->wql_next = next;
- next->wql_prev = prev;
- elt->link.wql_next = NULL;
- elt->link.wql_prev = NULL;
- return true;
- }
- else
- return false;
-}
-
-/* Notifies the first thread from a wait queue and dequeues it. */
-static void
-gl_waitqueue_notify_first (gl_waitqueue_t *wq)
-{
- if (wq->wq_list.wql_next != &wq->wq_list)
- {
- struct gl_waitqueue_element *elt =
- (struct gl_waitqueue_element *) wq->wq_list.wql_next;
- struct gl_waitqueue_link *prev;
- struct gl_waitqueue_link *next;
-
- /* Remove elt from the circular list. */
- prev = &wq->wq_list; /* = elt->link.wql_prev; */
- next = elt->link.wql_next;
- prev->wql_next = next;
- next->wql_prev = prev;
- elt->link.wql_next = NULL;
- elt->link.wql_prev = NULL;
-
- SetEvent (elt->event);
- /* After the SetEvent, this thread cannot access *elt any more, because
- the woken-up thread will quickly call free (elt). */
- }
-}
-
-/* Notifies all threads from a wait queue and dequeues them all. */
-static void
-gl_waitqueue_notify_all (gl_waitqueue_t *wq)
-{
- struct gl_waitqueue_link *l;
-
- for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
- {
- struct gl_waitqueue_element *elt = (struct gl_waitqueue_element *) l;
- struct gl_waitqueue_link *prev;
- struct gl_waitqueue_link *next;
-
- /* Remove elt from the circular list. */
- prev = &wq->wq_list; /* = elt->link.wql_prev; */
- next = elt->link.wql_next;
- prev->wql_next = next;
- next->wql_prev = prev;
- elt->link.wql_next = NULL;
- elt->link.wql_prev = NULL;
-
- SetEvent (elt->event);
- /* After the SetEvent, this thread cannot access *elt any more, because
- the woken-up thread will quickly call free (elt). */
-
- l = next;
- }
- if (!(wq->wq_list.wql_next == &wq->wq_list
- && wq->wq_list.wql_prev == &wq->wq_list))
- abort ();
-}
-
-int
-glthread_cond_init_func (gl_cond_t *cond)
-{
- InitializeCriticalSection (&cond->lock);
- gl_waitqueue_init (&cond->waiters);
-
- cond->guard.done = 1;
- return 0;
-}
-
-int
-glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
-{
- if (!cond->guard.done)
- {
- if (InterlockedIncrement (&cond->guard.started) == 0)
- /* This thread is the first one to need this condition variable.
- Initialize it. */
- glthread_cond_init (cond);
- else
- {
- /* Don't let cond->guard.started grow and wrap around. */
- InterlockedDecrement (&cond->guard.started);
- /* Yield the CPU while waiting for another thread to finish
- initializing this condition variable. */
- while (!cond->guard.done)
- Sleep (0);
- }
- }
-
- EnterCriticalSection (&cond->lock);
- {
- struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
- LeaveCriticalSection (&cond->lock);
- if (elt == NULL)
- {
- /* Allocation failure. Weird. */
- return EAGAIN;
- }
- else
- {
- HANDLE event = elt->event;
- int err;
- DWORD result;
-
- /* Now release the lock and let any other thread take it. */
- err = glthread_lock_unlock (lock);
- if (err != 0)
- {
- EnterCriticalSection (&cond->lock);
- gl_waitqueue_remove (&cond->waiters, elt);
- LeaveCriticalSection (&cond->lock);
- CloseHandle (event);
- free (elt);
- return err;
- }
- /* POSIX says:
- "If another thread is able to acquire the mutex after the
- about-to-block thread has released it, then a subsequent call to
- pthread_cond_broadcast() or pthread_cond_signal() in that thread
- shall behave as if it were issued after the about-to-block thread
- has blocked."
- This is fulfilled here, because the thread signalling is done
- through SetEvent, not PulseEvent. */
- /* Wait until another thread signals this event. */
- result = WaitForSingleObject (event, INFINITE);
- if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
- abort ();
- CloseHandle (event);
- free (elt);
- /* The thread which signalled the event already did the bookkeeping:
- removed us from the waiters. */
- return glthread_lock_lock (lock);
- }
- }
-}
-
-int
-glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
-{
- if (!cond->guard.done)
- {
- if (InterlockedIncrement (&cond->guard.started) == 0)
- /* This thread is the first one to need this condition variable.
- Initialize it. */
- glthread_cond_init (cond);
- else
- {
- /* Don't let cond->guard.started grow and wrap around. */
- InterlockedDecrement (&cond->guard.started);
- /* Yield the CPU while waiting for another thread to finish
- initializing this condition variable. */
- while (!cond->guard.done)
- Sleep (0);
- }
- }
-
- {
- struct timeval currtime;
-
- gettimeofday (&currtime, NULL);
- if (currtime.tv_sec > abstime->tv_sec
- || (currtime.tv_sec == abstime->tv_sec
- && currtime.tv_usec * 1000 >= abstime->tv_nsec))
- return ETIMEDOUT;
-
- EnterCriticalSection (&cond->lock);
- {
- struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
- LeaveCriticalSection (&cond->lock);
- if (elt == NULL)
- {
- /* Allocation failure. Weird. */
- return EAGAIN;
- }
- else
- {
- HANDLE event = elt->event;
- int err;
- DWORD timeout;
- DWORD result;
-
- /* Now release the lock and let any other thread take it. */
- err = glthread_lock_unlock (lock);
- if (err != 0)
- {
- EnterCriticalSection (&cond->lock);
- gl_waitqueue_remove (&cond->waiters, elt);
- LeaveCriticalSection (&cond->lock);
- CloseHandle (event);
- free (elt);
- return err;
- }
- /* POSIX says:
- "If another thread is able to acquire the mutex after the
- about-to-block thread has released it, then a subsequent call to
- pthread_cond_broadcast() or pthread_cond_signal() in that thread
- shall behave as if it were issued after the about-to-block thread
- has blocked."
- This is fulfilled here, because the thread signalling is done
- through SetEvent, not PulseEvent. */
- /* Wait until another thread signals this event or until the abstime
- passes. */
- gettimeofday (&currtime, NULL);
- if (currtime.tv_sec > abstime->tv_sec)
- timeout = 0;
- else
- {
- unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
- timeout = seconds * 1000;
- if (timeout / 1000 != seconds) /* overflow? */
- timeout = INFINITE;
- else
- {
- long milliseconds =
- abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
- if (milliseconds >= 0)
- {
- timeout += milliseconds;
- if (timeout < milliseconds) /* overflow? */
- timeout = INFINITE;
- }
- else
- {
- if (timeout >= - milliseconds)
- timeout -= (- milliseconds);
- else
- timeout = 0;
- }
- }
- }
- result = WaitForSingleObject (event, timeout);
- if (result == WAIT_FAILED)
- abort ();
- if (result == WAIT_TIMEOUT)
- {
- EnterCriticalSection (&cond->lock);
- if (gl_waitqueue_remove (&cond->waiters, elt))
- {
- /* The event was not signaled between the WaitForSingleObject
- call and the EnterCriticalSection call. */
- if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
- abort ();
- }
- else
- {
- /* The event was signaled between the WaitForSingleObject
- call and the EnterCriticalSection call. */
- if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
- abort ();
- /* Produce the right return value. */
- result = WAIT_OBJECT_0;
- }
- LeaveCriticalSection (&cond->lock);
- }
- else
- {
- /* The thread which signalled the event already did the
- bookkeeping: removed us from the waiters. */
- }
- CloseHandle (event);
- free (elt);
- /* Take the lock again. It does not matter whether this is done
- before or after the bookkeeping for WAIT_TIMEOUT. */
- err = glthread_lock_lock (lock);
- return (err ? err :
- result == WAIT_OBJECT_0 ? 0 :
- result == WAIT_TIMEOUT ? ETIMEDOUT :
- /* WAIT_FAILED shouldn't happen */ EAGAIN);
- }
- }
- }
-}
-
-int
-glthread_cond_signal_func (gl_cond_t *cond)
-{
- if (!cond->guard.done)
- return EINVAL;
-
- EnterCriticalSection (&cond->lock);
- /* POSIX says:
- "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
- have no effect if there are no threads currently blocked on cond." */
- if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
- gl_waitqueue_notify_first (&cond->waiters);
- LeaveCriticalSection (&cond->lock);
-
- return 0;
-}
-
-int
-glthread_cond_broadcast_func (gl_cond_t *cond)
-{
- if (!cond->guard.done)
- return EINVAL;
-
- EnterCriticalSection (&cond->lock);
- /* POSIX says:
- "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
- have no effect if there are no threads currently blocked on cond."
- gl_waitqueue_notify_all is a nop in this case. */
- gl_waitqueue_notify_all (&cond->waiters);
- LeaveCriticalSection (&cond->lock);
-
- return 0;
-}
-
-int
-glthread_cond_destroy_func (gl_cond_t *cond)
-{
- if (!cond->guard.done)
- return EINVAL;
- if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
- return EBUSY;
- DeleteCriticalSection (&cond->lock);
- cond->guard.done = 0;
- return 0;
-}
-
#endif
/* ========================================================================= */
diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h
index f252c32..8880b07 100644
--- a/lib/glthread/cond.h
+++ b/lib/glthread/cond.h
@@ -293,53 +293,38 @@ extern int glthread_cond_timedwait_multithreaded (gl_cond_t *cond, gl_lock_t *lo
# define WIN32_LEAN_AND_MEAN /* avoid including junk */
# include
+# include "windows-cond.h"
+
# ifdef __cplusplus
extern "C" {
# endif
/* -------------------------- gl_cond_t datatype -------------------------- */
-struct gl_waitqueue_link
-{
- struct gl_waitqueue_link *wql_next;
- struct gl_waitqueue_link *wql_prev;
-};
-typedef struct
- {
- struct gl_waitqueue_link wq_list; /* circular list of waiting threads */
- }
- gl_linked_waitqueue_t;
-typedef struct
- {
- glwthread_spinlock_t guard; /* protects the initialization */
- CRITICAL_SECTION lock; /* protects the remaining fields */
- gl_linked_waitqueue_t waiters; /* waiting threads */
- }
- gl_cond_t;
+typedef glwthread_cond_t gl_cond_t;
# define gl_cond_define(STORAGECLASS, NAME) \
STORAGECLASS gl_cond_t NAME;
# define gl_cond_define_initialized(STORAGECLASS, NAME) \
STORAGECLASS gl_cond_t NAME = gl_cond_initializer;
# define gl_cond_initializer \
- { { 0, -1 } }
+ GLWTHREAD_COND_INIT
# define glthread_cond_init(COND) \
- glthread_cond_init_func (COND)
+ glwthread_cond_init (COND)
# define glthread_cond_wait(COND, LOCK) \
- glthread_cond_wait_func (COND, LOCK)
+ glwthread_cond_wait (COND, LOCK, \
+ (int (*) (void *)) glwthread_mutex_lock, \
+ (int (*) (void *)) glwthread_mutex_unlock)
# define glthread_cond_timedwait(COND, LOCK, ABSTIME) \
- glthread_cond_timedwait_func (COND, LOCK, ABSTIME)
+ glwthread_cond_timedwait (COND, LOCK, \
+ (int (*) (void *)) glwthread_mutex_lock, \
+ (int (*) (void *)) glwthread_mutex_unlock, \
+ ABSTIME)
# define glthread_cond_signal(COND) \
- glthread_cond_signal_func (COND)
+ glwthread_cond_signal (COND)
# define glthread_cond_broadcast(COND) \
- glthread_cond_broadcast_func (COND)
+ glwthread_cond_broadcast (COND)
# define glthread_cond_destroy(COND) \
- glthread_cond_destroy_func (COND)
-extern int glthread_cond_init_func (gl_cond_t *cond);
-extern int glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock);
-extern int glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime);
-extern int glthread_cond_signal_func (gl_cond_t *cond);
-extern int glthread_cond_broadcast_func (gl_cond_t *cond);
-extern int glthread_cond_destroy_func (gl_cond_t *cond);
+ glwthread_cond_destroy (COND)
# ifdef __cplusplus
}
diff --git a/lib/windows-cond.c b/lib/windows-cond.c
new file mode 100644
index 0000000..1c1c68a
--- /dev/null
+++ b/lib/windows-cond.c
@@ -0,0 +1,425 @@
+/* Condition variables (native Windows implementation).
+ Copyright (C) 2008-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 . */
+
+/* Written by Yoann Vandoorselaere , 2008,
+ and Bruno Haible , 2008. */
+
+#include
+
+/* Specification. */
+#include "windows-cond.h"
+
+#include
+#include
+#include
+#include
+
+/* In this file, the waitqueues are implemented as linked lists. */
+#define glwthread_waitqueue_t glwthread_linked_waitqueue_t
+
+/* All links of a circular list, except the anchor, are of this type, carrying
+ a payload. */
+struct glwthread_waitqueue_element
+{
+ struct glwthread_waitqueue_link link; /* must be the first field! */
+ HANDLE event; /* Waiting thread, represented by an event.
+ This field is immutable once initialized. */
+};
+
+static void
+glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
+{
+ wq->wq_list.wql_next = &wq->wq_list;
+ wq->wq_list.wql_prev = &wq->wq_list;
+}
+
+/* Enqueues the current thread, represented by an event, in a wait queue.
+ Returns NULL if an allocation failure occurs. */
+static struct glwthread_waitqueue_element *
+glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
+{
+ struct glwthread_waitqueue_element *elt;
+ HANDLE event;
+
+ /* Allocate the memory for the waitqueue element on the heap, not on the
+ thread's stack. If the thread exits unexpectedly, we prefer to leak
+ some memory rather than to access unavailable memory and crash. */
+ elt =
+ (struct glwthread_waitqueue_element *)
+ malloc (sizeof (struct glwthread_waitqueue_element));
+ if (elt == NULL)
+ /* No more memory. */
+ return NULL;
+
+ /* 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. */
+ free (elt);
+ return NULL;
+ }
+ elt->event = event;
+ /* Insert elt at the end of the circular list. */
+ (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
+ (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
+ return elt;
+}
+
+/* Removes the current thread, represented by a
+ 'struct glwthread_waitqueue_element *', from a wait queue.
+ Returns true if is was found and removed, false if it was not present. */
+static bool
+glwthread_waitqueue_remove (glwthread_waitqueue_t *wq,
+ struct glwthread_waitqueue_element *elt)
+{
+ if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
+ {
+ /* Remove elt from the circular list. */
+ struct glwthread_waitqueue_link *prev = elt->link.wql_prev;
+ struct glwthread_waitqueue_link *next = elt->link.wql_next;
+ prev->wql_next = next;
+ next->wql_prev = prev;
+ elt->link.wql_next = NULL;
+ elt->link.wql_prev = NULL;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Notifies the first thread from a wait queue and dequeues it. */
+static void
+glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
+{
+ if (wq->wq_list.wql_next != &wq->wq_list)
+ {
+ struct glwthread_waitqueue_element *elt =
+ (struct glwthread_waitqueue_element *) wq->wq_list.wql_next;
+ struct glwthread_waitqueue_link *prev;
+ struct glwthread_waitqueue_link *next;
+
+ /* Remove elt from the circular list. */
+ prev = &wq->wq_list; /* = elt->link.wql_prev; */
+ next = elt->link.wql_next;
+ prev->wql_next = next;
+ next->wql_prev = prev;
+ elt->link.wql_next = NULL;
+ elt->link.wql_prev = NULL;
+
+ SetEvent (elt->event);
+ /* After the SetEvent, this thread cannot access *elt any more, because
+ the woken-up thread will quickly call free (elt). */
+ }
+}
+
+/* Notifies all threads from a wait queue and dequeues them all. */
+static void
+glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
+{
+ struct glwthread_waitqueue_link *l;
+
+ for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
+ {
+ struct glwthread_waitqueue_element *elt =
+ (struct glwthread_waitqueue_element *) l;
+ struct glwthread_waitqueue_link *prev;
+ struct glwthread_waitqueue_link *next;
+
+ /* Remove elt from the circular list. */
+ prev = &wq->wq_list; /* = elt->link.wql_prev; */
+ next = elt->link.wql_next;
+ prev->wql_next = next;
+ next->wql_prev = prev;
+ elt->link.wql_next = NULL;
+ elt->link.wql_prev = NULL;
+
+ SetEvent (elt->event);
+ /* After the SetEvent, this thread cannot access *elt any more, because
+ the woken-up thread will quickly call free (elt). */
+
+ l = next;
+ }
+ if (!(wq->wq_list.wql_next == &wq->wq_list
+ && wq->wq_list.wql_prev == &wq->wq_list))
+ abort ();
+}
+
+int
+glwthread_cond_init (glwthread_cond_t *cond)
+{
+ InitializeCriticalSection (&cond->lock);
+ glwthread_waitqueue_init (&cond->waiters);
+
+ cond->guard.done = 1;
+ return 0;
+}
+
+int
+glwthread_cond_wait (glwthread_cond_t *cond,
+ void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *))
+{
+ if (!cond->guard.done)
+ {
+ if (InterlockedIncrement (&cond->guard.started) == 0)
+ /* This thread is the first one to need this condition variable.
+ Initialize it. */
+ glwthread_cond_init (cond);
+ else
+ {
+ /* Don't let cond->guard.started grow and wrap around. */
+ InterlockedDecrement (&cond->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this condition variable. */
+ while (!cond->guard.done)
+ Sleep (0);
+ }
+ }
+
+ EnterCriticalSection (&cond->lock);
+ {
+ struct glwthread_waitqueue_element *elt =
+ glwthread_waitqueue_add (&cond->waiters);
+ LeaveCriticalSection (&cond->lock);
+ if (elt == NULL)
+ {
+ /* Allocation failure. Weird. */
+ return EAGAIN;
+ }
+ else
+ {
+ HANDLE event = elt->event;
+ int err;
+ DWORD result;
+
+ /* Now release the mutex and let any other thread take it. */
+ err = mutex_unlock (mutex);
+ if (err != 0)
+ {
+ EnterCriticalSection (&cond->lock);
+ glwthread_waitqueue_remove (&cond->waiters, elt);
+ LeaveCriticalSection (&cond->lock);
+ CloseHandle (event);
+ free (elt);
+ return err;
+ }
+ /* POSIX says:
+ "If another thread is able to acquire the mutex after the
+ about-to-block thread has released it, then a subsequent call to
+ pthread_cond_broadcast() or pthread_cond_signal() in that thread
+ shall behave as if it were issued after the about-to-block thread
+ has blocked."
+ This is fulfilled here, because the thread signalling is done
+ through SetEvent, not PulseEvent. */
+ /* Wait until another thread signals this event. */
+ result = WaitForSingleObject (event, INFINITE);
+ if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+ abort ();
+ CloseHandle (event);
+ free (elt);
+ /* The thread which signalled the event already did the bookkeeping:
+ removed us from the waiters. */
+ return mutex_lock (mutex);
+ }
+ }
+}
+
+int
+glwthread_cond_timedwait (glwthread_cond_t *cond,
+ void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *),
+ const struct timespec *abstime)
+{
+ if (!cond->guard.done)
+ {
+ if (InterlockedIncrement (&cond->guard.started) == 0)
+ /* This thread is the first one to need this condition variable.
+ Initialize it. */
+ glwthread_cond_init (cond);
+ else
+ {
+ /* Don't let cond->guard.started grow and wrap around. */
+ InterlockedDecrement (&cond->guard.started);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this condition variable. */
+ while (!cond->guard.done)
+ Sleep (0);
+ }
+ }
+
+ {
+ struct timeval currtime;
+
+ gettimeofday (&currtime, NULL);
+ if (currtime.tv_sec > abstime->tv_sec
+ || (currtime.tv_sec == abstime->tv_sec
+ && currtime.tv_usec * 1000 >= abstime->tv_nsec))
+ return ETIMEDOUT;
+
+ EnterCriticalSection (&cond->lock);
+ {
+ struct glwthread_waitqueue_element *elt =
+ glwthread_waitqueue_add (&cond->waiters);
+ LeaveCriticalSection (&cond->lock);
+ if (elt == NULL)
+ {
+ /* Allocation failure. Weird. */
+ return EAGAIN;
+ }
+ else
+ {
+ HANDLE event = elt->event;
+ int err;
+ DWORD timeout;
+ DWORD result;
+
+ /* Now release the mutex and let any other thread take it. */
+ err = mutex_unlock (mutex);
+ if (err != 0)
+ {
+ EnterCriticalSection (&cond->lock);
+ glwthread_waitqueue_remove (&cond->waiters, elt);
+ LeaveCriticalSection (&cond->lock);
+ CloseHandle (event);
+ free (elt);
+ return err;
+ }
+ /* POSIX says:
+ "If another thread is able to acquire the mutex after the
+ about-to-block thread has released it, then a subsequent call to
+ pthread_cond_broadcast() or pthread_cond_signal() in that thread
+ shall behave as if it were issued after the about-to-block thread
+ has blocked."
+ This is fulfilled here, because the thread signalling is done
+ through SetEvent, not PulseEvent. */
+ /* Wait until another thread signals this event or until the abstime
+ passes. */
+ gettimeofday (&currtime, NULL);
+ if (currtime.tv_sec > abstime->tv_sec)
+ timeout = 0;
+ else
+ {
+ unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+ timeout = seconds * 1000;
+ if (timeout / 1000 != seconds) /* overflow? */
+ timeout = INFINITE;
+ else
+ {
+ long milliseconds =
+ abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
+ if (milliseconds >= 0)
+ {
+ timeout += milliseconds;
+ if (timeout < milliseconds) /* overflow? */
+ timeout = INFINITE;
+ }
+ else
+ {
+ if (timeout >= - milliseconds)
+ timeout -= (- milliseconds);
+ else
+ timeout = 0;
+ }
+ }
+ }
+ result = WaitForSingleObject (event, timeout);
+ if (result == WAIT_FAILED)
+ abort ();
+ if (result == WAIT_TIMEOUT)
+ {
+ EnterCriticalSection (&cond->lock);
+ if (glwthread_waitqueue_remove (&cond->waiters, elt))
+ {
+ /* The event was not signaled between the WaitForSingleObject
+ call and the EnterCriticalSection call. */
+ if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
+ abort ();
+ }
+ else
+ {
+ /* The event was signaled between the WaitForSingleObject
+ call and the EnterCriticalSection call. */
+ if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
+ abort ();
+ /* Produce the right return value. */
+ result = WAIT_OBJECT_0;
+ }
+ LeaveCriticalSection (&cond->lock);
+ }
+ else
+ {
+ /* The thread which signalled the event already did the
+ bookkeeping: removed us from the waiters. */
+ }
+ CloseHandle (event);
+ free (elt);
+ /* Take the mutex again. It does not matter whether this is done
+ before or after the bookkeeping for WAIT_TIMEOUT. */
+ err = mutex_lock (mutex);
+ return (err ? err :
+ result == WAIT_OBJECT_0 ? 0 :
+ result == WAIT_TIMEOUT ? ETIMEDOUT :
+ /* WAIT_FAILED shouldn't happen */ EAGAIN);
+ }
+ }
+ }
+}
+
+int
+glwthread_cond_signal (glwthread_cond_t *cond)
+{
+ if (!cond->guard.done)
+ return EINVAL;
+
+ EnterCriticalSection (&cond->lock);
+ /* POSIX says:
+ "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
+ have no effect if there are no threads currently blocked on cond." */
+ if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
+ glwthread_waitqueue_notify_first (&cond->waiters);
+ LeaveCriticalSection (&cond->lock);
+
+ return 0;
+}
+
+int
+glwthread_cond_broadcast (glwthread_cond_t *cond)
+{
+ if (!cond->guard.done)
+ return EINVAL;
+
+ EnterCriticalSection (&cond->lock);
+ /* POSIX says:
+ "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
+ have no effect if there are no threads currently blocked on cond."
+ glwthread_waitqueue_notify_all is a nop in this case. */
+ glwthread_waitqueue_notify_all (&cond->waiters);
+ LeaveCriticalSection (&cond->lock);
+
+ return 0;
+}
+
+int
+glwthread_cond_destroy (glwthread_cond_t *cond)
+{
+ if (!cond->guard.done)
+ return EINVAL;
+ if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
+ return EBUSY;
+ DeleteCriticalSection (&cond->lock);
+ cond->guard.done = 0;
+ return 0;
+}
diff --git a/lib/windows-cond.h b/lib/windows-cond.h
new file mode 100644
index 0000000..a21a9d0
--- /dev/null
+++ b/lib/windows-cond.h
@@ -0,0 +1,74 @@
+/* Condition variables (native Windows implementation).
+ Copyright (C) 2008-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 . */
+
+/* Written by Yoann Vandoorselaere , 2008.
+ Based on Bruno Haible lock.h */
+
+#ifndef _WINDOWS_COND_H
+#define _WINDOWS_COND_H
+
+#define WIN32_LEAN_AND_MEAN /* avoid including junk */
+#include
+
+#include
+
+#include "windows-spinlock.h"
+
+struct glwthread_waitqueue_link
+{
+ struct glwthread_waitqueue_link *wql_next;
+ struct glwthread_waitqueue_link *wql_prev;
+};
+typedef struct
+ {
+ struct glwthread_waitqueue_link wq_list; /* circular list of waiting threads */
+ }
+ glwthread_linked_waitqueue_t;
+typedef struct
+ {
+ glwthread_spinlock_t guard; /* protects the initialization */
+ CRITICAL_SECTION lock; /* protects the remaining fields */
+ glwthread_linked_waitqueue_t waiters; /* waiting threads */
+ }
+ glwthread_cond_t;
+
+#define GLWTHREAD_COND_INIT { GLWTHREAD_SPINLOCK_INIT }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_cond_init (glwthread_cond_t *cond);
+/* Here, to cope with the various types of mutexes, the mutex is a 'void *', and
+ the caller needs to pass the corresponding *_lock and *_unlock functions. */
+extern int glwthread_cond_wait (glwthread_cond_t *cond,
+ void *mutex,
+ int (*mutex_lock) (void *),
+ int (*mutex_unlock) (void *));
+extern int glwthread_cond_timedwait (glwthread_cond_t *cond,
+ void *mutex,
+ int (*mutex_lock) (void *),
+ int (*mutex_unlock) (void *),
+ const struct timespec *abstime);
+extern int glwthread_cond_signal (glwthread_cond_t *cond);
+extern int glwthread_cond_broadcast (glwthread_cond_t *cond);
+extern int glwthread_cond_destroy (glwthread_cond_t *cond);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_COND_H */
diff --git a/modules/cond b/modules/cond
index c3a2dab..82b87e3 100644
--- a/modules/cond
+++ b/modules/cond
@@ -13,7 +13,7 @@ errno
extern-inline
stdbool
time
-gettimeofday
+windows-cond [test $gl_threads_api = windows]
configure.ac:
gl_COND
diff --git a/modules/windows-cond b/modules/windows-cond
new file mode 100644
index 0000000..afb70b0
--- /dev/null
+++ b/modules/windows-cond
@@ -0,0 +1,32 @@
+Description:
+Condition variables (native Windows implementation).
+
+Files:
+lib/windows-cond.h
+lib/windows-cond.c
+lib/windows-spinlock.h
+
+Depends-on:
+stdbool
+errno
+time
+gettimeofday
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+ mingw*)
+ AC_LIBOBJ([windows-cond])
+ ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-cond.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
--
2.7.4