From 1693e8678ba5527277147000462fc4e55866853e Mon Sep 17 00:00:00 2001 From: Stefan Eissing <icing@apache.org> Date: Thu, 6 Jul 2017 08:15:27 +0000 Subject: [PATCH] On the trunk: mod_http2: Simplify ready queue, less memory and better performance. Update mod_http2 version to 1.10.7. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1800978 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 +- modules/http2/h2_mplx.c | 21 +-- modules/http2/h2_mplx.h | 2 +- modules/http2/h2_util.c | 307 +++++++++++++++++++++++++++++++++++++ modules/http2/h2_util.h | 68 +++++++- modules/http2/h2_version.h | 2 +- 6 files changed, 390 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 4cacc23233..86cddde845 100644 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,8 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 - *) mod_http2: disable and give warning when mpm_prefork is encountered. The server will - continue to work, but HTTP/2 will no longer be negotiated. [Stefan Eissing] + *) mod_http2: Simplify ready queue, less memory and better performance. Update + mod_http2 version to 1.10.7. [Stefan Eissing] *) htpasswd / htdigest: Do not apply the strict permissions of the temporary passwd file to a possibly existing passwd file. PR 61240. [Ruediger Pluem] diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 8c5c71ca47..c360faf972 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -125,9 +125,9 @@ static void stream_cleanup(h2_mplx *m, h2_stream *stream) h2_stream_cleanup(stream); - h2_iq_remove(m->q, stream->id); - h2_fifo_remove(m->readyq, stream); h2_ihash_remove(m->streams, stream->id); + h2_iq_remove(m->q, stream->id); + h2_ififo_remove(m->readyq, stream->id); h2_ihash_add(m->shold, stream); if (!stream->task || stream->task->worker_done) { @@ -218,7 +218,7 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, m->spurge = h2_ihash_create(m->pool, offsetof(h2_stream,id)); m->q = h2_iq_create(m->pool, m->max_streams); - status = h2_fifo_set_create(&m->readyq, m->pool, m->max_streams); + status = h2_ififo_set_create(&m->readyq, m->pool, m->max_streams); if (status != APR_SUCCESS) { apr_pool_destroy(m->pool); return NULL; @@ -626,7 +626,7 @@ apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, static void check_data_for(h2_mplx *m, h2_stream *stream, int lock) { - if (h2_fifo_push(m->readyq, stream) == APR_SUCCESS) { + if (h2_ififo_push(m->readyq, stream->id) == APR_SUCCESS) { apr_atomic_set32(&m->event_pending, 1); H2_MPLX_ENTER_MAYBE(m, lock); if (m->added_output) { @@ -1232,7 +1232,7 @@ apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, void *on_ctx) { h2_stream *stream; - int n; + int n, id; ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, "h2_mplx(%ld): dispatch events", m->id); @@ -1242,11 +1242,14 @@ apr_status_t h2_mplx_dispatch_master_events(h2_mplx *m, h2_ihash_iter(m->streams, report_consumption_iter, m); purge_streams(m, 1); - n = h2_fifo_count(m->readyq); + n = h2_ififo_count(m->readyq); while (n > 0 - && (h2_fifo_try_pull(m->readyq, (void**)&stream) == APR_SUCCESS)) { + && (h2_ififo_try_pull(m->readyq, &id) == APR_SUCCESS)) { --n; - on_resume(on_ctx, stream); + stream = h2_ihash_get(m->streams, id); + if (stream) { + on_resume(on_ctx, stream); + } } return APR_SUCCESS; @@ -1267,7 +1270,7 @@ int h2_mplx_awaits_data(h2_mplx *m) if (h2_ihash_empty(m->streams)) { waiting = 0; } - else if (!m->tasks_active && !h2_fifo_count(m->readyq) + else if (!m->tasks_active && !h2_ififo_count(m->readyq) && h2_iq_empty(m->q)) { waiting = 0; } diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index d56c65289c..04ba7a8d77 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -69,7 +69,7 @@ struct h2_mplx { struct h2_ihash_t *spurge; /* all streams done, ready for destroy */ struct h2_iqueue *q; /* all stream ids that need to be started */ - struct h2_fifo *readyq; /* all streams ready for output */ + struct h2_ififo *readyq; /* all stream ids ready for output */ struct h2_ihash_t *redo_tasks; /* all tasks that need to be redone */ diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 04a9311424..e1879cb200 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -881,6 +881,313 @@ apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem) return rv; } +/******************************************************************************* + * FIFO int queue + ******************************************************************************/ + +struct h2_ififo { + int *elems; + int nelems; + int set; + int head; + int count; + int aborted; + apr_thread_mutex_t *lock; + apr_thread_cond_t *not_empty; + apr_thread_cond_t *not_full; +}; + +static int inth_index(h2_ififo *fifo, int n) +{ + return (fifo->head + n) % fifo->nelems; +} + +static apr_status_t ififo_destroy(void *data) +{ + h2_ififo *fifo = data; + + apr_thread_cond_destroy(fifo->not_empty); + apr_thread_cond_destroy(fifo->not_full); + apr_thread_mutex_destroy(fifo->lock); + + return APR_SUCCESS; +} + +static int iindex_of(h2_ififo *fifo, int id) +{ + int i; + + for (i = 0; i < fifo->count; ++i) { + if (id == fifo->elems[inth_index(fifo, i)]) { + return i; + } + } + return -1; +} + +static apr_status_t icreate_int(h2_ififo **pfifo, apr_pool_t *pool, + int capacity, int as_set) +{ + apr_status_t rv; + h2_ififo *fifo; + + fifo = apr_pcalloc(pool, sizeof(*fifo)); + if (fifo == NULL) { + return APR_ENOMEM; + } + + rv = apr_thread_mutex_create(&fifo->lock, + APR_THREAD_MUTEX_UNNESTED, pool); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_thread_cond_create(&fifo->not_empty, pool); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_thread_cond_create(&fifo->not_full, pool); + if (rv != APR_SUCCESS) { + return rv; + } + + fifo->elems = apr_pcalloc(pool, capacity * sizeof(int)); + if (fifo->elems == NULL) { + return APR_ENOMEM; + } + fifo->nelems = capacity; + fifo->set = as_set; + + *pfifo = fifo; + apr_pool_cleanup_register(pool, fifo, ififo_destroy, apr_pool_cleanup_null); + + return APR_SUCCESS; +} + +apr_status_t h2_ififo_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity) +{ + return icreate_int(pfifo, pool, capacity, 0); +} + +apr_status_t h2_ififo_set_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity) +{ + return icreate_int(pfifo, pool, capacity, 1); +} + +apr_status_t h2_ififo_term(h2_ififo *fifo) +{ + apr_status_t rv; + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + fifo->aborted = 1; + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} + +apr_status_t h2_ififo_interrupt(h2_ififo *fifo) +{ + apr_status_t rv; + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + apr_thread_cond_broadcast(fifo->not_empty); + apr_thread_cond_broadcast(fifo->not_full); + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} + +int h2_ififo_count(h2_ififo *fifo) +{ + return fifo->count; +} + +static apr_status_t icheck_not_empty(h2_ififo *fifo, int block) +{ + while (fifo->count == 0) { + if (!block) { + return APR_EAGAIN; + } + if (fifo->aborted) { + return APR_EOF; + } + apr_thread_cond_wait(fifo->not_empty, fifo->lock); + } + return APR_SUCCESS; +} + +static apr_status_t ififo_push_int(h2_ififo *fifo, int id, int block) +{ + if (fifo->aborted) { + return APR_EOF; + } + + if (fifo->set && iindex_of(fifo, id) >= 0) { + /* set mode, elem already member */ + return APR_EEXIST; + } + else if (fifo->count == fifo->nelems) { + if (block) { + while (fifo->count == fifo->nelems) { + if (fifo->aborted) { + return APR_EOF; + } + apr_thread_cond_wait(fifo->not_full, fifo->lock); + } + } + else { + return APR_EAGAIN; + } + } + + ap_assert(fifo->count < fifo->nelems); + fifo->elems[inth_index(fifo, fifo->count)] = id; + ++fifo->count; + if (fifo->count == 1) { + apr_thread_cond_broadcast(fifo->not_empty); + } + return APR_SUCCESS; +} + +static apr_status_t ififo_push(h2_ififo *fifo, int id, int block) +{ + apr_status_t rv; + + if (fifo->aborted) { + return APR_EOF; + } + + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = ififo_push_int(fifo, id, block); + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} + +apr_status_t h2_ififo_push(h2_ififo *fifo, int id) +{ + return ififo_push(fifo, id, 1); +} + +apr_status_t h2_ififo_try_push(h2_ififo *fifo, int id) +{ + return ififo_push(fifo, id, 0); +} + +static apr_status_t ipull_head(h2_ififo *fifo, int *pi, int block) +{ + apr_status_t rv; + + if ((rv = icheck_not_empty(fifo, block)) != APR_SUCCESS) { + *pi = 0; + return rv; + } + *pi = fifo->elems[fifo->head]; + --fifo->count; + if (fifo->count > 0) { + fifo->head = inth_index(fifo, 1); + if (fifo->count+1 == fifo->nelems) { + apr_thread_cond_broadcast(fifo->not_full); + } + } + return APR_SUCCESS; +} + +static apr_status_t ififo_pull(h2_ififo *fifo, int *pi, int block) +{ + apr_status_t rv; + + if (fifo->aborted) { + return APR_EOF; + } + + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + rv = ipull_head(fifo, pi, block); + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} + +apr_status_t h2_ififo_pull(h2_ififo *fifo, int *pi) +{ + return ififo_pull(fifo, pi, 1); +} + +apr_status_t h2_ififo_try_pull(h2_ififo *fifo, int *pi) +{ + return ififo_pull(fifo, pi, 0); +} + +static apr_status_t ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx, int block) +{ + apr_status_t rv; + int id; + + if (fifo->aborted) { + return APR_EOF; + } + + if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) { + if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) { + switch (fn(id, ctx)) { + case H2_FIFO_OP_PULL: + break; + case H2_FIFO_OP_REPUSH: + rv = ififo_push_int(fifo, id, block); + break; + } + } + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} + +apr_status_t h2_ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx) +{ + return ififo_peek(fifo, fn, ctx, 1); +} + +apr_status_t h2_ififo_try_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx) +{ + return ififo_peek(fifo, fn, ctx, 0); +} + +apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) +{ + apr_status_t rv; + + if (fifo->aborted) { + return APR_EOF; + } + + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { + int i, rc; + int e; + + rc = 0; + for (i = 0; i < fifo->count; ++i) { + e = fifo->elems[inth_index(fifo, i)]; + if (e == id) { + ++rc; + } + else if (rc) { + fifo->elems[inth_index(fifo, i-rc)] = e; + } + } + if (rc) { + fifo->count -= rc; + if (fifo->count + rc == fifo->nelems) { + apr_thread_cond_broadcast(fifo->not_full); + } + rv = APR_SUCCESS; + } + else { + rv = APR_EAGAIN; + } + + apr_thread_mutex_unlock(fifo->lock); + } + return rv; +} /******************************************************************************* * h2_util for apt_table_t diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h index 35163716ad..b2c25f69cc 100644 --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h @@ -185,7 +185,7 @@ size_t h2_iq_mshift(h2_iqueue *q, int *pint, size_t max); int h2_iq_contains(h2_iqueue *q, int sid); /******************************************************************************* - * FIFO queue + * FIFO queue (void* elements) ******************************************************************************/ /** @@ -255,6 +255,72 @@ apr_status_t h2_fifo_try_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx); */ apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem); +/******************************************************************************* + * iFIFO queue (int elements) + ******************************************************************************/ + +/** + * A thread-safe FIFO queue with some extra bells and whistles, if you + * do not need anything special, better use 'apr_queue'. + */ +typedef struct h2_ififo h2_ififo; + +/** + * Create a FIFO queue that can hold up to capacity int. ints can + * appear several times. + */ +apr_status_t h2_ififo_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity); + +/** + * Create a FIFO set that can hold up to capacity integers. Ints only + * appear once. Pushing an int already present does not change the + * queue and is successful. + */ +apr_status_t h2_ififo_set_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity); + +apr_status_t h2_ififo_term(h2_ififo *fifo); +apr_status_t h2_ififo_interrupt(h2_ififo *fifo); + +int h2_ififo_count(h2_ififo *fifo); + +/** + * Push an int into the queue. Blocks if there is no capacity left. + * + * @param fifo the FIFO queue + * @param id the int to push + * @return APR_SUCCESS on push, APR_EAGAIN on try_push on a full queue, + * APR_EEXIST when in set mode and elem already there. + */ +apr_status_t h2_ififo_push(h2_ififo *fifo, int id); +apr_status_t h2_ififo_try_push(h2_ififo *fifo, int id); + +apr_status_t h2_ififo_pull(h2_ififo *fifo, int *pi); +apr_status_t h2_ififo_try_pull(h2_ififo *fifo, int *pi); + +typedef h2_fifo_op_t h2_ififo_peek_fn(int head, void *ctx); + +/** + * Call given function on the head of the queue, once it exists, and + * perform the returned operation on it. The queue will hold its lock during + * this time, so no other operations on the queue are possible. + * @param fifo the queue to peek at + * @param fn the function to call on the head, once available + * @param ctx context to pass in call to function + */ +apr_status_t h2_ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx); + +/** + * Non-blocking version of h2_fifo_peek. + */ +apr_status_t h2_ififo_try_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx); + +/** + * Remove the integer from the queue, will remove multiple appearances. + * @param id the integer to remove + * @return APR_SUCCESS iff > 0 ints were removed, APR_EAGAIN otherwise. + */ +apr_status_t h2_ififo_remove(h2_ififo *fifo, int id); + /******************************************************************************* * common helpers ******************************************************************************/ diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 7b447d3c19..da284faf4e 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -26,7 +26,7 @@ * @macro * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "1.10.7-DEV" +#define MOD_HTTP2_VERSION "1.10.7" /** * @macro -- 2.40.0