2008-10-15 Bruno Haible New implementation of condition variables for Win32. * lib/glthread/cond.h (struct gl_waitqueue_link): New type. (gl_linked_waitqueue_t): New type. (gl_cond_t): Use it. * lib/glthread/cond.c (struct gl_waitqueue_element): New type. (gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove, gl_waitqueue_notify_first, gl_waitqueue_notify_all): New functions. (glthread_cond_init_func, glthread_cond_wait_func, glthread_cond_timedwait_func, glthread_cond_signal_func, glthread_cond_broadcast_func, glthread_cond_destroy_func): Reimplemented on the basis of gl_linked_waitqueue_t. * lib/glthread/lock.h (gl_carray_waitqueue_t): Renamed from gl_waitqueue_t. (gl_rwlock_t): Update. * lib/glthread/lock.c (gl_waitqueue_t): Alias to gl_carray_waitqueue_t. *** lib/glthread/cond.h.orig 2008-10-16 02:03:37.000000000 +0200 --- lib/glthread/cond.h 2008-10-15 04:10:05.000000000 +0200 *************** *** 279,296 **** /* -------------------------- 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) \ --- 279,299 ---- /* -------------------------- 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 { gl_spinlock_t guard; /* protects the initialization */ CRITICAL_SECTION lock; /* protects the remaining fields */ ! gl_linked_waitqueue_t waiters; /* waiting threads */ } gl_cond_t; # define gl_cond_define(STORAGECLASS, NAME) \ *** lib/glthread/cond.c.orig 2008-10-16 02:03:37.000000000 +0200 --- lib/glthread/cond.c 2008-10-16 01:36:03.000000000 +0200 *************** *** 79,101 **** /* -------------------------- 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; } --- 79,221 ---- /* -------------------------- 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 inline 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 inline 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 inline 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 inline 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; } *************** *** 115,189 **** 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. It does not matter whether this is done ! before or after the bookkeeping. */ ! 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 : ! result == WAIT_OBJECT_0 ? 0 : ! /* WAIT_FAILED shouldn't happen */ EAGAIN); } - } } } --- 235,284 ---- 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); } } } *************** *** 211,317 **** 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. It does not matter whether this is done ! before or after the bookkeeping. */ ! 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 : result == WAIT_OBJECT_0 ? 0 : result == WAIT_TIMEOUT ? ETIMEDOUT : /* WAIT_FAILED shouldn't happen */ EAGAIN); } - } } } --- 306,415 ---- 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 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); } } } *************** *** 324,341 **** 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; --- 422,430 ---- 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; *************** *** 350,365 **** 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; --- 439,447 ---- 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; *************** *** 370,378 **** { 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; --- 452,459 ---- { 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; *** lib/glthread/lock.h.orig 2008-10-16 02:03:37.000000000 +0200 --- lib/glthread/lock.h 2008-10-15 02:48:13.000000000 +0200 *************** *** 675,687 **** unsigned int alloc; /* length of allocated array */ unsigned int offset; /* index of first waiting thread in array */ } ! gl_waitqueue_t; typedef struct { gl_spinlock_t guard; /* protects the initialization */ CRITICAL_SECTION lock; /* protects the remaining fields */ ! gl_waitqueue_t waiting_readers; /* waiting readers */ ! gl_waitqueue_t waiting_writers; /* waiting writers */ int runcount; /* number of readers running, or -1 when a writer runs */ } gl_rwlock_t; --- 675,687 ---- unsigned int alloc; /* length of allocated array */ unsigned int offset; /* index of first waiting thread in array */ } ! gl_carray_waitqueue_t; typedef struct { gl_spinlock_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; *** lib/glthread/lock.c.orig 2008-10-16 02:03:37.000000000 +0200 --- lib/glthread/lock.c 2008-10-15 02:51:16.000000000 +0200 *************** *** 680,685 **** --- 680,688 ---- /* ------------------------- gl_rwlock_t datatype ------------------------- */ + /* In this file, the waitqueues are implemented as circular arrays. */ + #define gl_waitqueue_t gl_carray_waitqueue_t + static inline void gl_waitqueue_init (gl_waitqueue_t *wq) {