--- build/patches/win/APR/locks/win32/thread_cond.c.orig 2006-09-22 15:15:55.000000000 +0400 +++ build/patches/win/APR/locks/win32/thread_cond.c 2006-09-22 15:16:48.000000000 +0400 @@ -22,81 +22,91 @@ #include "win32/apr_arch_thread_cond.h" #include "apr_portable.h" +// define this constants to synchronized waiting queue access +// it is neccessery becuase signal() is not requiried to hold mutex +#define LOCK_QUEUE apr_thread_mutex_lock(cond->queue_mutex) +#define UNLOCK_QUEUE apr_thread_mutex_unlock(cond->queue_mutex) + static apr_status_t thread_cond_cleanup(void *data) { - apr_thread_cond_t *cond = data; - CloseHandle(cond->event); - return APR_SUCCESS; + apr_thread_mutex_destroy(((apr_thread_cond_t *)data)->queue_mutex); + return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_thread_cond_create(apr_thread_cond_t **cond, apr_pool_t *pool) { - *cond = apr_palloc(pool, sizeof(**cond)); + *cond = apr_pcalloc(pool, sizeof(**cond)); (*cond)->pool = pool; - (*cond)->event = CreateEvent(NULL, TRUE, FALSE, NULL); - (*cond)->signal_all = 0; - (*cond)->num_waiting = 0; - - (*cond)->wait_level = 0; - (*cond)->notify_level = 0; - + (*cond)-> dummy_node.next = (*cond)-> dummy_node.prev = &((*cond)-> dummy_node); + apr_thread_mutex_create(&((*cond)->queue_mutex), APR_THREAD_MUTEX_NESTED, pool); return APR_SUCCESS; } -// this is used to skip staled notifies, -// ingnor notify that arrived before wait, V VVVVV - int overflow check. -#define NOT_STALED(wait,notify) (wait<=notify || (wait > 0x4fffffff && notify < 0xffffff)) +static void _enqueue (apr_thread_cond_t *cond, struct waiting_node *node) { + node->next = &(cond->dummy_node); + node->prev = cond->dummy_node.prev; + node->prev->next = node; + cond->dummy_node.prev = node; + +} + +static int _remove_from_queue (apr_thread_cond_t *cond, struct waiting_node *node) { + if (node->next == NULL || node->prev == NULL) { + // already dequeued (by signal) + return -1; + } + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = NULL; + node->prev = NULL; + return 0; +} + +//dequeue +// return NULL if queue is empty +static struct waiting_node* _dequeue (apr_thread_cond_t *cond) { + struct waiting_node* node; + if (cond->dummy_node.next == &(cond->dummy_node)) { + // the queue is empty + return NULL; + } + node=cond->dummy_node.next; + _remove_from_queue (cond,node); + return node; +} static APR_INLINE apr_status_t _thread_cond_timedwait(apr_thread_cond_t *cond, apr_thread_mutex_t *mutex, DWORD timeout_ms ) { DWORD res; - unsigned this_wait_level; + apr_status_t rv = APR_SUCCESS; + struct waiting_node node; + struct waiting_node *tmp; - cond->wait_level++; - this_wait_level=cond->wait_level; - - while (1) { - cond->num_waiting++; - apr_thread_mutex_unlock(mutex); - res = WaitForSingleObject(cond->event, timeout_ms); - apr_thread_mutex_lock(mutex); - cond->num_waiting--; - if (res != WAIT_OBJECT_0) { - apr_status_t rv = apr_get_os_error(); + // add waiting + node.event = CreateEvent(NULL, TRUE, FALSE, NULL); + LOCK_QUEUE; + _enqueue (cond, &node); + UNLOCK_QUEUE; + + // release mutex and wait for signal + apr_thread_mutex_unlock(mutex); + res = WaitForSingleObject(node.event, timeout_ms); + apr_thread_mutex_lock(mutex); + if (res != WAIT_OBJECT_0) { if (res == WAIT_TIMEOUT) { - return APR_TIMEUP; - } - return apr_get_os_error(); + rv = APR_TIMEUP; + } else { + rv = apr_get_os_error(); + } } - if (cond->signal_all) { - if (cond->num_waiting == 0) { - cond->signal_all = 0; - cond->signalled = 0; - ResetEvent(cond->event); - } - - // wait till all NOT_STALLED wait procceed and reset the event - // then continue waiting - while (!NOT_STALED(this_wait_level, cond->notify_level) && cond->num_waiting != 0) { - apr_thread_mutex_unlock(mutex); - apr_thread_yield(); - apr_thread_mutex_lock(mutex); - } - - if(NOT_STALED(this_wait_level, cond->notify_level)) { - break; - } + LOCK_QUEUE; + _remove_from_queue (cond, &node); + CloseHandle(node.event); + UNLOCK_QUEUE; - } - else if (cond->signalled && NOT_STALED(this_wait_level, cond->notify_level)) { - cond->signalled = 0; - ResetEvent(cond->event); - break; - } - } - return APR_SUCCESS; + return rv; } APR_DECLARE(apr_status_t) apr_thread_cond_wait(apr_thread_cond_t *cond, @@ -118,28 +128,34 @@ { apr_status_t rv = APR_SUCCESS; DWORD res; + struct waiting_node* node; - cond->signalled = 1; - cond->notify_level = cond->wait_level; - res = SetEvent(cond->event); - if (res == 0) { - rv = apr_get_os_error(); - } + LOCK_QUEUE; + node = _dequeue (cond); + if (node != NULL) { + res = SetEvent(node->event); + if (res == 0) { + rv = apr_get_os_error(); + } + } + UNLOCK_QUEUE; return rv; } APR_DECLARE(apr_status_t) apr_thread_cond_broadcast(apr_thread_cond_t *cond) { apr_status_t rv = APR_SUCCESS; - DWORD res; + DWORD res; + struct waiting_node* node; - cond->signalled = 1; - cond->signal_all = 1; - cond->notify_level = cond->wait_level; - res = SetEvent(cond->event); - if (res == 0) { - rv = apr_get_os_error(); - } + LOCK_QUEUE; + for (node = _dequeue (cond); node != NULL; node = _dequeue (cond)) { + res = SetEvent(node->event); + if (res == 0) { + rv = apr_get_os_error(); + } + } + UNLOCK_QUEUE; return rv; } --- build/patches/win/APR/include/arch/win32/apr_arch_thread_cond.h.orig 2006-09-22 15:15:17.000000000 +0400 +++ build/patches/win/APR/include/arch/win32/apr_arch_thread_cond.h 2006-09-22 15:18:57.000000000 +0400 @@ -19,14 +19,23 @@ #include "apr_thread_cond.h" + +struct waiting_node { + // notification event + HANDLE event; + // double-linked queue + struct waiting_node *prev; + struct waiting_node *next; +}; + +// queue based condition implementation struct apr_thread_cond_t { apr_pool_t *pool; - HANDLE event; - int signal_all; - int num_waiting; - int signalled; - unsigned wait_level; - unsigned notify_level; + // the signal, could be called without mutex + // so we use internal one to guard the waiting queue + apr_thread_mutex_t *queue_mutex; + // head-tail marker node + struct waiting_node dummy_node; }; #endif /* THREAD_COND_H */