From 4116861d97496a3e87a5ee74f2e40843bd1e1a7a Mon Sep 17 00:00:00 2001 From: Yoann Vandoorselaere Date: Sun, 12 Oct 2008 00:14:22 +0200 Subject: [PATCH] Provide a Win32 implementation of the 'cond' module. --- ChangeLog | 11 ++ lib/glthread/cond.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/glthread/cond.h | 59 +++++++++- modules/cond | 1 + 4 files changed, 379 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 49d6258e0..dd1b73d92 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-10-11 Yoann Vandoorselaere + Bruno Haible + + Provide a Win32 implementation of the 'cond' module. + * lib/glthread/cond.h [USE_WIN32]: New implementation. + * lib/glthread/cond.c (glthread_cond_init_func, + glthread_cond_wait_func, glthread_cond_timedwait_func, + glthread_cond_signal_func, glthread_cond_broadcast_func, + glthread_cond_destroy_func) [USE_WIN32]: New functions. + * modules/cond (Dependencies): Add gettimeofday. + 2008-10-11 Bruno Haible Make sleep work on older versions of mingw. diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c index e3073d586..c35272e7c 100644 --- a/lib/glthread/cond.c +++ b/lib/glthread/cond.c @@ -15,7 +15,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* Written by Yoann Vandoorselaere , 2008. */ +/* Written by Yoann Vandoorselaere , 2008, + and Bruno Haible , 2008. */ #include @@ -71,3 +72,310 @@ glthread_cond_timedwait_multithreaded (gl_cond_t *cond, #endif /* ========================================================================= */ + +#if USE_WIN32_THREADS + +#include + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +/* This implementation is based on the article + Douglas C. Schmidt, Irfan Pyarali + "Strategies for Implementing POSIX Condition Variables on Win32" + */ + +int +glthread_cond_init_func (gl_cond_t *cond) +{ + InitializeCriticalSection (&cond->lock); + /* Create a manual-reset event. */ + cond->event = CreateEvent (NULL, TRUE, FALSE, NULL); + cond->waiters_count = 0; + cond->release_count = 0; + cond->wait_generation_count = 0; + + 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 + /* Yield the CPU while waiting for another thread to finish + initializing this condition variable. */ + while (!cond->guard.done) + Sleep (0); + } + + { + unsigned old_generation_count; + HANDLE event; + + EnterCriticalSection (&cond->lock); + /* Increment waiters_count, + and get a copy of the current wait_generation_count. */ + cond->waiters_count++; + old_generation_count = cond->wait_generation_count; + event = cond->event; + LeaveCriticalSection (&cond->lock); + + { + /* Now release the lock and let any other thread take it. */ + int err = glthread_lock_unlock (lock); + if (err != 0) + { + EnterCriticalSection (&cond->lock); + cond->waiters_count--; + LeaveCriticalSection (&cond->lock); + return err; + } + + { + /* Wait until another thread signals this event. */ + DWORD result; + + for (;;) + { + bool wait_done; + + result = WaitForSingleObject (event, INFINITE); + if (result != WAIT_OBJECT_0) + break; + /* Distingish intended from spurious wakeups. */ + EnterCriticalSection (&cond->lock); + wait_done = + (cond->release_count > 0 + && cond->wait_generation_count != old_generation_count); + LeaveCriticalSection (&cond->lock); + if (wait_done) + break; + } + + /* Take the lock again. */ + err = glthread_lock_lock (lock); + + /* Do the bookkeeping. */ + EnterCriticalSection (&cond->lock); + cond->waiters_count--; + if (result == WAIT_OBJECT_0) + { + /* The wait terminated because the event was signaled. + Acknowledge the receipt. */ + if (--cond->release_count == 0) + { + /* The last waiting thread to be notified has to reset + the event. */ + ResetEvent (cond->event); + } + } + LeaveCriticalSection (&cond->lock); + + return (err ? err : + ret == WAIT_OBJECT_0 ? 0 : + /* WAIT_FAILED shouldn't happen */ EAGAIN); + } + } + } +} + +int +glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime) +{ + 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; + + 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 + /* Yield the CPU while waiting for another thread to finish + initializing this condition variable. */ + while (!cond->guard.done) + Sleep (0); + } + + { + unsigned old_generation_count; + HANDLE event; + + EnterCriticalSection (&cond->lock); + /* Increment waiters_count, + and get a copy of the current wait_generation_count. */ + cond->waiters_count++; + old_generation_count = cond->wait_generation_count; + event = cond->event; + LeaveCriticalSection (&cond->lock); + + { + /* Now release the lock and let any other thread take it. */ + int err = glthread_lock_unlock (lock); + if (err != 0) + { + EnterCriticalSection (&cond->lock); + cond->waiters_count--; + LeaveCriticalSection (&cond->lock); + return err; + } + + { + /* Wait until another thread signals this event or until the abstime + passes. */ + DWORD result; + + for (;;) + { + DWORD timeout; + bool wait_done; + + 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_OBJECT_0) + break; + /* Distingish intended from spurious wakeups. */ + EnterCriticalSection (&cond->lock); + wait_done = + (cond->release_count > 0 + && cond->wait_generation_count != old_generation_count); + LeaveCriticalSection (&cond->lock); + if (wait_done) + break; + } + + /* Take the lock again. */ + err = glthread_lock_lock (lock); + + /* Do the bookkeeping. */ + EnterCriticalSection (&cond->lock); + cond->waiters_count--; + if (result == WAIT_OBJECT_0) + { + /* The wait terminated because the event was signaled. + Acknowledge the receipt. */ + if (--cond->release_count == 0) + { + /* The last waiting thread to be notified has to reset + the event. */ + ResetEvent (cond->event); + } + } + LeaveCriticalSection (&cond->lock); + + return (err ? err : + ret == WAIT_OBJECT_0 ? 0 : + ret == 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." + Also, if waiters_count == release_count > 0, all blocked threads will + be unblocked soon anyway; do nothing in this case as well. */ + if (cond->waiters_count > cond->release_count) + { + /* Signal the manual-reset event. */ + SetEvent (cond->event); + /* ... and reset it is soon as one of the currently waiting threads has + acknowledged the receipt of the signal. */ + cond->release_count++; + cond->wait_generation_count++; + } + 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." */ + if (cond->waiters_count > 0) + { + /* Signal the manual-reset event. */ + SetEvent (cond->event); + /* ... and reset it only after all currently waiting threads have + acknowledged the receipt of the signal. */ + cond->release_count = cond->waiters_count; + cond->wait_generation_count++; + } + LeaveCriticalSection (&cond->lock); + + return 0; +} + +int +glthread_cond_destroy_func (gl_cond_t *cond) +{ + if (!cond->guard.done) + return EINVAL; + if (cond->waiters_count > 0) + return EBUSY; + CloseHandle (cond->event); + DeleteCriticalSection (&cond->lock); + cond->guard.done = 0; + return 0; +} + +#endif + +/* ========================================================================= */ diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h index e6707b85e..fca1cbf40 100644 --- a/lib/glthread/cond.h +++ b/lib/glthread/cond.h @@ -269,7 +269,64 @@ extern int glthread_cond_timedwait_multithreaded (gl_cond_t *cond, gl_lock_t *lo /* ========================================================================= */ -#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS) +#if USE_WIN32_THREADS + +# include + +# ifdef __cplusplus +extern "C" { +# endif + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +typedef struct + { + gl_spinlock_t guard; /* protects the initialization */ + CRITICAL_SECTION lock; /* protects the remaining fields */ + HANDLE event; /* an event configured for manual reset */ + unsigned int waiters_count; /* number of threads currently waiting */ + unsigned int release_count; /* number of threads that are currently + being notified but have not yet + acknowledged. Always + release_count <= waiters_count */ + unsigned int wait_generation_count; /* incremented each time a signal + or broadcast is performed */ + } + 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 } } +# define glthread_cond_init(COND) \ + glthread_cond_init_func (COND) +# define glthread_cond_wait(COND, LOCK) \ + glthread_cond_wait_func (COND, LOCK) +# define glthread_cond_timedwait(COND, LOCK, ABSTIME) \ + glthread_cond_timedwait_func (COND, LOCK, ABSTIME) +# define glthread_cond_signal(COND) \ + glthread_cond_signal_func (COND) +# define glthread_cond_broadcast(COND) \ + glthread_cond_broadcast_func (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); + +# ifdef __cplusplus +} +# endif + +#endif + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) /* Provide dummy implementation if threads are not supported. */ diff --git a/modules/cond b/modules/cond index 7a1dd5d82..a28ed116d 100644 --- a/modules/cond +++ b/modules/cond @@ -12,6 +12,7 @@ lock errno stdbool time +gettimeofday configure.ac: gl_COND -- 2.11.0