/* Condition variables for multithreading.
- Copyright (C) 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 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
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program; if not, see <http://www.gnu.org/licenses/>. */
/* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
and Bruno Haible <bruno@clisp.org>, 2008. */
#include <config.h>
+#define _GLTHREAD_COND_INLINE _GL_EXTERN_INLINE
#include "glthread/cond.h"
/* ========================================================================= */
int
glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
- gl_lock_t *lock,
- struct timespec *abstime)
+ gl_lock_t *lock,
+ struct timespec *abstime)
{
int ret, status;
pth_event_t ev;
int
glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
- gl_lock_t *lock,
- struct timespec *abstime)
+ gl_lock_t *lock,
+ struct timespec *abstime)
{
int ret;
/* ========================================================================= */
-#if USE_WIN32_THREADS
+#if USE_WINDOWS_THREADS
#include <sys/time.h>
This field is immutable once initialized. */
};
-static inline void
+static void
gl_waitqueue_init (gl_waitqueue_t *wq)
{
wq->wq_list.wql_next = &wq->wq_list;
/* 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 inline bool
+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)
}
/* Notifies the first thread from a wait queue and dequeues it. */
-static inline void
+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_element *) wq->wq_list.wql_next;
struct gl_waitqueue_link *prev;
struct gl_waitqueue_link *next;
SetEvent (elt->event);
/* After the SetEvent, this thread cannot access *elt any more, because
- the woken-up thread will quickly call free (elt). */
+ the woken-up thread will quickly call free (elt). */
}
}
/* Notifies all threads from a wait queue and dequeues them all. */
-static inline void
+static void
gl_waitqueue_notify_all (gl_waitqueue_t *wq)
{
struct gl_waitqueue_link *l;
SetEvent (elt->event);
/* After the SetEvent, this thread cannot access *elt any more, because
- the woken-up thread will quickly call free (elt). */
+ the woken-up thread will quickly call free (elt). */
l = next;
}
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);
+ /* This thread is the first one to need this condition variable.
+ Initialize it. */
+ glthread_cond_init (cond);
else
- /* Yield the CPU while waiting for another thread to finish
- initializing this condition variable. */
- while (!cond->guard.done)
- Sleep (0);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this condition variable. */
+ while (!cond->guard.done)
+ Sleep (0);
}
EnterCriticalSection (&cond->lock);
LeaveCriticalSection (&cond->lock);
if (elt == NULL)
{
- /* Allocation failure. Weird. */
- return EAGAIN;
+ /* 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);
+ 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);
}
}
}
gettimeofday (&currtime, NULL);
if (currtime.tv_sec > abstime->tv_sec
|| (currtime.tv_sec == abstime->tv_sec
- && currtime.tv_usec * 1000 >= abstime->tv_nsec))
+ && currtime.tv_usec * 1000 >= abstime->tv_nsec))
return ETIMEDOUT;
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);
+ /* This thread is the first one to need this condition variable.
+ Initialize it. */
+ glthread_cond_init (cond);
else
- /* Yield the CPU while waiting for another thread to finish
- initializing this condition variable. */
- while (!cond->guard.done)
- Sleep (0);
+ /* Yield the CPU while waiting for another thread to finish
+ initializing this condition variable. */
+ while (!cond->guard.done)
+ Sleep (0);
}
EnterCriticalSection (&cond->lock);
LeaveCriticalSection (&cond->lock);
if (elt == NULL)
{
- /* Allocation failure. Weird. */
- return EAGAIN;
+ /* 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);
+ 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);
}
}
}