From 05266ad52ef0c1848a81b21705b9f13fc8f7028b Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 14 Mar 2014 01:35:26 +0500 Subject: [PATCH 8/8] Add apr_queue_pop_timeout() This function will pop or get an object from the queue, blocking if the queue is already empty for up to N microseconds. Signed-off-by: Anthony Minessale Signed-off-by: Travis Cross --- include/apr_queue.h | 18 +++++++++++++++ util-misc/apr_queue.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/include/apr_queue.h b/include/apr_queue.h index 1d09e9d..e7544d6 100644 --- a/include/apr_queue.h +++ b/include/apr_queue.h @@ -25,9 +25,11 @@ * Although condition variables are sometimes available without threads. */ +#include "apr.h" #include "apu.h" #include "apr_errno.h" #include "apr_pools.h" +#include "apr_time.h" #if APR_HAS_THREADS @@ -79,6 +81,22 @@ APR_DECLARE(apr_status_t) apr_queue_push(apr_queue_t *queue, void *data); APR_DECLARE(apr_status_t) apr_queue_pop(apr_queue_t *queue, void **data); /** + * pop/get an object from the queue, blocking if the queue is already empty for up to N microseconds + * + * @param queue the queue + * @param data the data + * @param timeout The amount of time in microseconds to wait. This is + * a maximum, not a minimum. If the condition is signaled, we + * will wake up before this time, otherwise the error APR_TIMEUP + * is returned. + * @returns APR_TIMEUP the request timed out + * @returns APR_EINTR the blocking was interrupted (try again) + * @returns APR_EOF if the queue has been terminated + * @returns APR_SUCCESS on a successfull pop + */ +APR_DECLARE(apr_status_t) apr_queue_pop_timeout(apr_queue_t *queue, void **data, apr_interval_time_t timeout); + +/** * push/add an object to the queue, returning immediately if the queue is full * * @param queue the queue diff --git a/util-misc/apr_queue.c b/util-misc/apr_queue.c index 0972493..ef2edfd 100644 --- a/util-misc/apr_queue.c +++ b/util-misc/apr_queue.c @@ -322,6 +322,70 @@ APR_DECLARE(apr_status_t) apr_queue_pop(apr_queue_t *queue, void **data) /** * Retrieves the next item from the queue. If there are no + * items available, it will block until one becomes available, or + * until timeout is elapsed. Once retrieved, the item is placed into + * the address specified by'data'. + */ +APR_DECLARE(apr_status_t) apr_queue_pop_timeout(apr_queue_t *queue, void **data, apr_interval_time_t timeout) +{ + apr_status_t rv; + + if (queue->terminated) { + return APR_EOF; /* no more elements ever again */ + } + + rv = apr_thread_mutex_lock(queue->one_big_mutex); + if (rv != APR_SUCCESS) { + return rv; + } + + /* Keep waiting until we wake up and find that the queue is not empty. */ + if (apr_queue_empty(queue)) { + if (!queue->terminated) { + queue->empty_waiters++; + rv = apr_thread_cond_timedwait(queue->not_empty, queue->one_big_mutex, timeout); + queue->empty_waiters--; + /* In the event of a timemout, APR_TIMEUP will be returned */ + if (rv != APR_SUCCESS) { + apr_thread_mutex_unlock(queue->one_big_mutex); + return rv; + } + } + /* If we wake up and it's still empty, then we were interrupted */ + if (apr_queue_empty(queue)) { + Q_DBG("queue empty (intr)", queue); + rv = apr_thread_mutex_unlock(queue->one_big_mutex); + if (rv != APR_SUCCESS) { + return rv; + } + if (queue->terminated) { + return APR_EOF; /* no more elements ever again */ + } + else { + return APR_EINTR; + } + } + } + + *data = queue->data[queue->out]; + queue->nelts--; + + queue->out = (queue->out + 1) % queue->bounds; + if (queue->full_waiters) { + Q_DBG("signal !full", queue); + rv = apr_thread_cond_signal(queue->not_full); + if (rv != APR_SUCCESS) { + apr_thread_mutex_unlock(queue->one_big_mutex); + return rv; + } + } + + rv = apr_thread_mutex_unlock(queue->one_big_mutex); + return rv; +} + +/** + * Retrieves the next item from the queue. If there are no * items available, return APR_EAGAIN. Once retrieved, * the item is placed into the address specified by 'data'. */ -- 2.0.0.rc4