Commit 66cb2500 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

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
parent c5d37191
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
                                                         -*- 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]
+12 −9
Original line number Diff line number Diff line
@@ -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,12 +1242,15 @@ 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;
        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;
    }
+1 −1
Original line number Diff line number Diff line
@@ -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 */
    
+307 −0
Original line number Diff line number Diff line
@@ -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
+67 −1
Original line number Diff line number Diff line
@@ -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
 ******************************************************************************/
Loading