Mercurial > hg > octave-lojdl > gnulib-hg
changeset 10627:04756a28484d
Provide a Win32 implementation of the 'cond' module.
author | Yoann Vandoorselaere <yoann@prelude-ids.org> |
---|---|
date | Sun, 12 Oct 2008 00:14:22 +0200 |
parents | 4d2d469f9c49 |
children | b40075443afa |
files | ChangeLog lib/glthread/cond.c lib/glthread/cond.h modules/cond |
diffstat | 4 files changed, 379 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-10-11 Yoann Vandoorselaere <yoann@prelude-ids.org> + Bruno Haible <bruno@clisp.org> + + 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 <bruno@clisp.org> Make sleep work on older versions of mingw.
--- 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 <yoann@prelude-ids.org>, 2008. */ +/* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008, + and Bruno Haible <bruno@clisp.org>, 2008. */ #include <config.h> @@ -71,3 +72,310 @@ #endif /* ========================================================================= */ + +#if USE_WIN32_THREADS + +#include <sys/time.h> + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +/* This implementation is based on the article + Douglas C. Schmidt, Irfan Pyarali + "Strategies for Implementing POSIX Condition Variables on Win32" + <http://www.cs.wustl.edu/~schmidt/win32-cv-1.html> */ + +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 + +/* ========================================================================= */
--- a/lib/glthread/cond.h +++ b/lib/glthread/cond.h @@ -269,7 +269,64 @@ /* ========================================================================= */ -#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS) +#if USE_WIN32_THREADS + +# include <windows.h> + +# 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. */