You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2016/03/02 12:21:29 UTC

svn commit: r1733259 [4/4] - in /httpd/httpd/branches/2.4.x: ./ modules/http2/

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.h Wed Mar  2 11:21:28 2016
@@ -16,6 +16,9 @@
 #ifndef __mod_h2__h2_util__
 #define __mod_h2__h2_util__
 
+/*******************************************************************************
+ * some debugging/format helpers
+ ******************************************************************************/
 struct h2_request;
 struct nghttp2_frame;
 
@@ -28,10 +31,53 @@ size_t h2_util_header_print(char *buffer
 
 void h2_util_camel_case_header(char *s, size_t len);
 
-int h2_req_ignore_header(const char *name, size_t len);
-int h2_req_ignore_trailer(const char *name, size_t len);
-void h2_req_strip_ignored_header(apr_table_t *headers);
-int h2_res_ignore_trailer(const char *name, size_t len);
+int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
+
+/*******************************************************************************
+ * ihash - hash for structs with int identifier
+ ******************************************************************************/
+typedef struct h2_ihash_t h2_ihash_t;
+typedef int h2_ihash_iter_t(void *ctx, void *val);
+
+/**
+ * Create a hash for structures that have an identifying int member.
+ * @param pool the pool to use
+ * @param offset_of_int the offsetof() the int member in the struct
+ */
+h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int);
+
+size_t h2_ihash_count(h2_ihash_t *ih);
+int h2_ihash_is_empty(h2_ihash_t *ih);
+void *h2_ihash_get(h2_ihash_t *ih, int id);
+
+/**
+ * Iterate over the hash members (without defined order) and invoke
+ * fn for each member until 0 is returned.
+ * @param ih the hash to iterate over
+ * @param fn the function to invoke on each member
+ * @param ctx user supplied data passed into each iteration call
+ */
+void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx);
+
+void h2_ihash_add(h2_ihash_t *ih, void *val);
+void h2_ihash_remove(h2_ihash_t *ih, int id);
+void h2_ihash_clear(h2_ihash_t *ih);
+ 
+/*******************************************************************************
+ * common helpers
+ ******************************************************************************/
+/* h2_log2(n) iff n is a power of 2 */
+unsigned char h2_log2(apr_uint32_t n);
+
+/**
+ * Count the bytes that all key/value pairs in a table have
+ * in length (exlucding terminating 0s), plus additional extra per pair.
+ *
+ * @param t the table to inspect
+ * @param pair_extra the extra amount to add per pair
+ * @return the number of bytes all key/value pairs have
+ */
+apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra);
 
 /**
  * Return != 0 iff the string s contains the token, as specified in
@@ -42,6 +88,32 @@ int h2_util_contains_token(apr_pool_t *p
 const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, 
                                       const char *tokens[], apr_size_t len);
 
+/** Match a header value against a string constance, case insensitive */
+#define H2_HD_MATCH_LIT(l, name, nlen)  \
+    ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+
+/*******************************************************************************
+ * HTTP/2 header helpers
+ ******************************************************************************/
+int h2_req_ignore_header(const char *name, size_t len);
+int h2_req_ignore_trailer(const char *name, size_t len);
+int h2_res_ignore_trailer(const char *name, size_t len);
+int h2_proxy_res_ignore_header(const char *name, size_t len);
+
+/**
+ * Set the push policy for the given request. Takes request headers into 
+ * account, see draft https://tools.ietf.org/html/draft-ruellan-http-accept-push-policy-00
+ * for details.
+ * 
+ * @param req the request to determine the policy for
+ * @param p the pool to use
+ * @param push_enabled if HTTP/2 server push is generally enabled for this request
+ */
+void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled);
+
+/*******************************************************************************
+ * base64 url encoding, different table from normal base64
+ ******************************************************************************/
 /**
  * I always wanted to write my own base64url decoder...not. See 
  * https://tools.ietf.org/html/rfc4648#section-5 for description.
@@ -52,8 +124,9 @@ apr_size_t h2_util_base64url_decode(cons
 const char *h2_util_base64url_encode(const char *data, 
                                      apr_size_t len, apr_pool_t *pool);
 
-#define H2_HD_MATCH_LIT(l, name, nlen)  \
-    ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
+/*******************************************************************************
+ * nghttp2 helpers
+ ******************************************************************************/
 
 #define H2_HD_MATCH_LIT_CS(l, name)  \
     ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
@@ -87,6 +160,9 @@ h2_ngheader *h2_util_ngheader_make_res(a
 h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, 
                                        const struct h2_request *req);
 
+/*******************************************************************************
+ * apr brigade helpers
+ ******************************************************************************/
 /**
  * Moves data from one brigade into another. If maxlen > 0, it only
  * moves up to maxlen bytes into the target brigade, making bucket splits

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_version.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_version.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_version.h Wed Mar  2 11:21:28 2016
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.2.2"
+#define MOD_HTTP2_VERSION "1.3.2"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x010202
+#define MOD_HTTP2_VERSION_NUM 0x010302
 
 
 #endif /* mod_h2_h2_version_h */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_worker.c Wed Mar  2 11:21:28 2016
@@ -22,81 +22,43 @@
 #include <http_core.h>
 #include <http_log.h>
 
+#include "h2.h"
 #include "h2_private.h"
 #include "h2_conn.h"
 #include "h2_ctx.h"
 #include "h2_h2.h"
 #include "h2_mplx.h"
-#include "h2_request.h"
 #include "h2_task.h"
 #include "h2_worker.h"
 
 static void* APR_THREAD_FUNC execute(apr_thread_t *thread, void *wctx)
 {
     h2_worker *worker = (h2_worker *)wctx;
-    apr_status_t status;
-    
-    (void)thread;
-    /* Other code might want to see a socket for this connection this
-     * worker processes. Allocate one without further function...
-     */
-    status = apr_socket_create(&worker->socket,
-                               APR_INET, SOCK_STREAM,
-                               APR_PROTO_TCP, worker->pool);
-    if (status != APR_SUCCESS) {
-        ap_log_perror(APLOG_MARK, APLOG_ERR, status, worker->pool,
-                      APLOGNO(02948) "h2_worker(%d): alloc socket", 
-                      worker->id);
-        worker->worker_done(worker, worker->ctx);
-        return NULL;
-    }
+    int sticky;
     
     while (!worker->aborted) {
-        h2_mplx *m;
-        const h2_request *req;
+        h2_task *task;
         
-        /* Get a h2_mplx + h2_request from the main workers queue. */
-        status = worker->get_next(worker, &m, &req, worker->ctx);
-        
-        while (req) {
-            conn_rec *c, *master = m->c;
-            int stream_id = req->id;
+        /* Get a h2_task from the main workers queue. */
+        worker->get_next(worker, worker->ctx, &task, &sticky);
+        while (task) {
+            h2_task_do(task, worker->io);
             
-            c = h2_slave_create(master, worker->task_pool, 
-                                worker->thread, worker->socket);
-            if (!c) {
-                ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, c,
-                              APLOGNO(02957) "h2_request(%ld-%d): error setting up slave connection", 
-                              m->id, stream_id);
-                h2_mplx_out_rst(m, stream_id, H2_ERR_INTERNAL_ERROR);
+            /* if someone was waiting on this task, time to wake up */
+            apr_thread_cond_signal(worker->io);
+            /* report the task done and maybe get another one from the same
+             * mplx (= master connection), if we can be sticky. 
+             */
+            if (sticky && !worker->aborted) {
+                h2_mplx_task_done(task->mplx, task, &task);
             }
             else {
-                h2_task *task;
-                
-                task = h2_task_create(m->id, req, worker->task_pool, m);
-                h2_ctx_create_for(c, task);
-                h2_task_do(task, c, worker->io, worker->socket);
+                h2_mplx_task_done(task->mplx, task, NULL);
                 task = NULL;
-                
-                apr_thread_cond_signal(worker->io);
             }
-            
-            /* clean our references and report request as done. Signal
-             * that we want another unless we have been aborted */
-            /* TODO: this will keep a worker attached to this h2_mplx as
-             * long as it has requests to handle. Might no be fair to
-             * other mplx's. Perhaps leave after n requests? */
-            req = NULL;
-            apr_pool_clear(worker->task_pool);
-            h2_mplx_request_done(&m, stream_id, worker->aborted? NULL : &req);
         }
     }
 
-    if (worker->socket) {
-        apr_socket_close(worker->socket);
-        worker->socket = NULL;
-    }
-    
     worker->worker_done(worker, worker->ctx);
     return NULL;
 }
@@ -116,6 +78,7 @@ h2_worker *h2_worker_create(int id,
     apr_allocator_create(&allocator);
     apr_allocator_max_free_set(allocator, ap_max_mem_free);
     apr_pool_create_ex(&pool, parent_pool, NULL, allocator);
+    apr_pool_tag(pool, "h2_worker");
     apr_allocator_owner_set(allocator, pool);
 
     w = apr_pcalloc(pool, sizeof(h2_worker));
@@ -134,7 +97,6 @@ h2_worker *h2_worker_create(int id,
             return NULL;
         }
         
-        apr_pool_create(&w->task_pool, w->pool);
         apr_thread_create(&w->thread, attr, execute, w, w->pool);
     }
     return w;
@@ -173,13 +135,4 @@ int h2_worker_is_aborted(h2_worker *work
     return worker->aborted;
 }
 
-h2_task *h2_worker_create_task(h2_worker *worker, h2_mplx *m, 
-                               const h2_request *req)
-{
-    h2_task *task;
-    
-    task = h2_task_create(m->id, req, worker->task_pool, m);
-    return task;
-}
-
 

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_worker.h Wed Mar  2 11:21:28 2016
@@ -30,9 +30,9 @@ typedef struct h2_worker h2_worker;
  * until a h2_mplx becomes available or the worker itself
  * gets aborted (idle timeout, for example). */
 typedef apr_status_t h2_worker_mplx_next_fn(h2_worker *worker,
-                                            struct h2_mplx **pm,
-                                            const struct h2_request **preq,
-                                            void *ctx);
+                                            void *ctx,
+                                            struct h2_task **ptask,
+                                            int *psticky);
 
 /* Invoked just before the worker thread exits. */
 typedef void h2_worker_done_fn(h2_worker *worker, void *ctx);
@@ -45,9 +45,7 @@ struct h2_worker {
     int id;
     apr_thread_t *thread;
     apr_pool_t *pool;
-    apr_pool_t *task_pool;
     struct apr_thread_cond_t *io;
-    apr_socket_t *socket;
     
     h2_worker_mplx_next_fn *get_next;
     h2_worker_done_fn *worker_done;
@@ -142,7 +140,4 @@ int h2_worker_get_id(h2_worker *worker);
 
 int h2_worker_is_aborted(h2_worker *worker);
 
-struct h2_task *h2_worker_create_task(h2_worker *worker, struct h2_mplx *m, 
-                                      const struct h2_request *req);
-                                      
 #endif /* defined(__mod_h2__h2_worker__) */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_workers.c Wed Mar  2 11:21:28 2016
@@ -23,10 +23,10 @@
 #include <http_core.h>
 #include <http_log.h>
 
+#include "h2.h"
 #include "h2_private.h"
 #include "h2_mplx.h"
-#include "h2_request.h"
-#include "h2_task_queue.h"
+#include "h2_task.h"
 #include "h2_worker.h"
 #include "h2_workers.h"
 
@@ -61,124 +61,137 @@ static void cleanup_zombies(h2_workers *
     }
 }
 
+static h2_task *next_task(h2_workers *workers)
+{
+    h2_task *task = NULL;
+    h2_mplx *last = NULL;
+    int has_more;
+    
+    /* Get the next h2_mplx to process that has a task to hand out.
+     * If it does, place it at the end of the queu and return the
+     * task to the worker.
+     * If it (currently) has no tasks, remove it so that it needs
+     * to register again for scheduling.
+     * If we run out of h2_mplx in the queue, we need to wait for
+     * new mplx to arrive. Depending on how many workers do exist,
+     * we do a timed wait or block indefinitely.
+     */
+    while (!task && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
+        h2_mplx *m = H2_MPLX_LIST_FIRST(&workers->mplxs);
+        
+        if (last == m) {
+            break;
+        }
+        H2_MPLX_REMOVE(m);
+        --workers->mplx_count;
+        
+        task = h2_mplx_pop_task(m, &has_more);
+        if (has_more) {
+            H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+            ++workers->mplx_count;
+            if (!last) {
+                last = m;
+            }
+        }
+    }
+    return task;
+}
+
 /**
  * Get the next task for the given worker. Will block until a task arrives
  * or the max_wait timer expires and more than min workers exist.
- * The previous h2_mplx instance might be passed in and will be served
- * with preference, since we can ask it for the next task without aquiring
- * the h2_workers lock.
  */
-static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, 
-                                  const h2_request **preq, void *ctx)
+static apr_status_t get_mplx_next(h2_worker *worker, void *ctx, 
+                                  h2_task **ptask, int *psticky)
 {
     apr_status_t status;
-    apr_time_t max_wait, start_wait;
-    h2_workers *workers = (h2_workers *)ctx;
+    apr_time_t wait_until = 0, now;
+    h2_workers *workers = ctx;
+    h2_task *task = NULL;
     
-    max_wait = apr_time_from_sec(apr_atomic_read32(&workers->max_idle_secs));
-    start_wait = apr_time_now();
+    *ptask = NULL;
+    *psticky = 0;
     
     status = apr_thread_mutex_lock(workers->lock);
     if (status == APR_SUCCESS) {
-        const h2_request *req = NULL;
-        h2_mplx *m = NULL;
-        int has_more = 0;
-
-        ++workers->idle_worker_count;
+        ++workers->idle_workers;
         ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
                      "h2_worker(%d): looking for work", h2_worker_get_id(worker));
         
-        while (!req && !h2_worker_is_aborted(worker) && !workers->aborted) {
-            
-            /* Get the next h2_mplx to process that has a task to hand out.
-             * If it does, place it at the end of the queu and return the
-             * task to the worker.
-             * If it (currently) has no tasks, remove it so that it needs
-             * to register again for scheduling.
-             * If we run out of h2_mplx in the queue, we need to wait for
-             * new mplx to arrive. Depending on how many workers do exist,
-             * we do a timed wait or block indefinitely.
-             */
-            m = NULL;
-            while (!req && !H2_MPLX_LIST_EMPTY(&workers->mplxs)) {
-                m = H2_MPLX_LIST_FIRST(&workers->mplxs);
-                H2_MPLX_REMOVE(m);
+        while (!h2_worker_is_aborted(worker) && !workers->aborted
+               && !(task = next_task(workers))) {
+        
+            /* Need to wait for a new tasks to arrive. If we are above
+             * minimum workers, we do a timed wait. When timeout occurs
+             * and we have still more workers, we shut down one after
+             * the other. */
+            cleanup_zombies(workers, 0);
+            if (workers->worker_count > workers->min_workers) {
+                now = apr_time_now();
+                if (now >= wait_until) {
+                    wait_until = now + apr_time_from_sec(workers->max_idle_secs);
+                }
                 
-                req = h2_mplx_pop_request(m, &has_more);
-                if (req) {
-                    if (has_more) {
-                        H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
-                    }
-                    else {
-                        has_more = !H2_MPLX_LIST_EMPTY(&workers->mplxs);
-                    }
+                ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
+                             "h2_worker(%d): waiting signal, "
+                             "workers=%d, idle=%d", worker->id, 
+                             (int)workers->worker_count, 
+                             workers->idle_workers);
+                status = apr_thread_cond_timedwait(workers->mplx_added,
+                                                   workers->lock, 
+                                                   wait_until - now);
+                if (status == APR_TIMEUP
+                    && workers->worker_count > workers->min_workers) {
+                    /* waited long enough without getting a task and
+                     * we are above min workers, abort this one. */
+                    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, 
+                                 workers->s,
+                                 "h2_workers: aborting idle worker");
+                    h2_worker_abort(worker);
                     break;
                 }
             }
-            
-            if (!req) {
-                /* Need to wait for a new mplx to arrive.
-                 */
-                cleanup_zombies(workers, 0);
-                
-                if (workers->worker_count > workers->min_size) {
-                    apr_time_t now = apr_time_now();
-                    if (now >= (start_wait + max_wait)) {
-                        /* waited long enough without getting a task. */
-                        if (workers->worker_count > workers->min_size) {
-                            ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, 
-                                         workers->s,
-                                         "h2_workers: aborting idle worker");
-                            h2_worker_abort(worker);
-                            break;
-                        }
-                    }
-                    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
-                                 "h2_worker(%d): waiting signal, "
-                                 "worker_count=%d", worker->id, 
-                                 (int)workers->worker_count);
-                    apr_thread_cond_timedwait(workers->mplx_added,
-                                              workers->lock, max_wait);
-                }
-                else {
-                    ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
-                                 "h2_worker(%d): waiting signal (eternal), "
-                                 "worker_count=%d", worker->id, 
-                                 (int)workers->worker_count);
-                    apr_thread_cond_wait(workers->mplx_added, workers->lock);
-                }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
+                             "h2_worker(%d): waiting signal (eternal), "
+                             "worker_count=%d, idle=%d", worker->id, 
+                             (int)workers->worker_count,
+                             workers->idle_workers);
+                apr_thread_cond_wait(workers->mplx_added, workers->lock);
             }
         }
         
-        /* Here, we either have gotten task and mplx for the worker or
-         * needed to give up with more than enough workers.
+        /* Here, we either have gotten task or decided to shut down
+         * the calling worker.
          */
-        if (req) {
-            ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
-                         "h2_worker(%d): start request(%ld-%d)",
-                         h2_worker_get_id(worker), m->id, req->id);
-            *pm = m;
-            *preq = req;
+        if (task) {
+            /* Ok, we got something to give back to the worker for execution. 
+             * If we have more idle workers than h2_mplx in our queue, then
+             * we let the worker be sticky, e.g. making it poll the task's
+             * h2_mplx instance for more work before asking back here.
+             * This avoids entering our global lock as long as enough idle
+             * workers remain. Stickiness of a worker ends when the connection
+             * has no new tasks to process, so the worker will get back here
+             * eventually.
+             */
+            *ptask = task;
+            *psticky = (workers->max_workers >= workers->mplx_count);
             
-            if (has_more && workers->idle_worker_count > 1) {
+            if (workers->mplx_count && workers->idle_workers > 1) {
                 apr_thread_cond_signal(workers->mplx_added);
             }
-            status = APR_SUCCESS;
-        }
-        else {
-            status = APR_EOF;
         }
         
-        --workers->idle_worker_count;
+        --workers->idle_workers;
         apr_thread_mutex_unlock(workers->lock);
     }
     
-    return status;
+    return *ptask? APR_SUCCESS : APR_EOF;
 }
 
 static void worker_done(h2_worker *worker, void *ctx)
 {
-    h2_workers *workers = (h2_workers *)ctx;
+    h2_workers *workers = ctx;
     apr_status_t status = apr_thread_mutex_lock(workers->lock);
     if (status == APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
@@ -213,7 +226,7 @@ static apr_status_t h2_workers_start(h2_
         ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
                       "h2_workers: starting");
 
-        while (workers->worker_count < workers->min_size
+        while (workers->worker_count < workers->min_workers
                && status == APR_SUCCESS) {
             status = add_worker(workers);
         }
@@ -223,7 +236,7 @@ static apr_status_t h2_workers_start(h2_
 }
 
 h2_workers *h2_workers_create(server_rec *s, apr_pool_t *server_pool,
-                              int min_size, int max_size,
+                              int min_workers, int max_workers,
                               apr_size_t max_tx_handles)
 {
     apr_status_t status;
@@ -239,13 +252,14 @@ h2_workers *h2_workers_create(server_rec
      * happen on the pool handed to us, which we do not guard.
      */
     apr_pool_create(&pool, server_pool);
+    apr_pool_tag(pool, "h2_workers");
     workers = apr_pcalloc(pool, sizeof(h2_workers));
     if (workers) {
         workers->s = s;
         workers->pool = pool;
-        workers->min_size = min_size;
-        workers->max_size = max_size;
-        apr_atomic_set32(&workers->max_idle_secs, 10);
+        workers->min_workers = min_workers;
+        workers->max_workers = max_workers;
+        workers->max_idle_secs = 10;
         
         workers->max_tx_handles = max_tx_handles;
         workers->spare_tx_handles = workers->max_tx_handles;
@@ -320,22 +334,22 @@ apr_status_t h2_workers_register(h2_work
     apr_status_t status = apr_thread_mutex_lock(workers->lock);
     if (status == APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_TRACE3, status, workers->s,
-                     "h2_workers: register mplx(%ld)", m->id);
+                     "h2_workers: register mplx(%ld), idle=%d", 
+                     m->id, workers->idle_workers);
         if (in_list(workers, m)) {
-            ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
-                         "h2_workers: already registered mplx(%ld)", m->id);
             status = APR_EAGAIN;
         }
         else {
             H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
+            ++workers->mplx_count;
             status = APR_SUCCESS;
         }
         
-        if (workers->idle_worker_count > 0) { 
+        if (workers->idle_workers > 0) { 
             apr_thread_cond_signal(workers->mplx_added);
         }
         else if (status == APR_SUCCESS 
-                 && workers->worker_count < workers->max_size) {
+                 && workers->worker_count < workers->max_workers) {
             ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, workers->s,
                          "h2_workers: got %d worker, adding 1", 
                          workers->worker_count);
@@ -368,7 +382,7 @@ void h2_workers_set_max_idle_secs(h2_wor
                      " is not valid, ignored.", idle_secs);
         return;
     }
-    apr_atomic_set32(&workers->max_idle_secs, idle_secs);
+    workers->max_idle_secs = idle_secs;
 }
 
 apr_size_t h2_workers_tx_reserve(h2_workers *workers, apr_size_t count)

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_workers.h Wed Mar  2 11:21:28 2016
@@ -27,7 +27,6 @@ struct apr_thread_cond_t;
 struct h2_mplx;
 struct h2_request;
 struct h2_task;
-struct h2_task_queue;
 
 typedef struct h2_workers h2_workers;
 
@@ -36,8 +35,11 @@ struct h2_workers {
     apr_pool_t *pool;
     
     int next_worker_id;
-    int min_size;
-    int max_size;
+    int min_workers;
+    int max_workers;
+    int worker_count;
+    int idle_workers;
+    int max_idle_secs;
     
     apr_size_t max_tx_handles;
     apr_size_t spare_tx_handles;
@@ -49,10 +51,7 @@ struct h2_workers {
     APR_RING_HEAD(h2_worker_list, h2_worker) workers;
     APR_RING_HEAD(h2_worker_zombies, h2_worker) zombies;
     APR_RING_HEAD(h2_mplx_list, h2_mplx) mplxs;
-    
-    int worker_count;
-    volatile apr_uint32_t max_idle_secs;
-    volatile apr_uint32_t idle_worker_count;
+    int mplx_count;
     
     struct apr_thread_mutex_t *lock;
     struct apr_thread_cond_t *mplx_added;

Modified: httpd/httpd/branches/2.4.x/modules/http2/mod_http2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/mod_http2.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/mod_http2.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/mod_http2.c Wed Mar  2 11:21:28 2016
@@ -15,6 +15,7 @@
 
 #include <apr_optional.h>
 #include <apr_optional_hooks.h>
+#include <apr_time.h>
 #include <apr_want.h>
 
 #include <httpd.h>
@@ -34,6 +35,7 @@
 #include "h2_config.h"
 #include "h2_ctx.h"
 #include "h2_h2.h"
+#include "h2_mplx.h"
 #include "h2_push.h"
 #include "h2_request.h"
 #include "h2_switch.h"
@@ -71,7 +73,7 @@ static int h2_post_config(apr_pool_t *p,
                           apr_pool_t *ptemp, server_rec *s)
 {
     void *data = NULL;
-    const char *mod_h2_init_key = "mod_h2_init_counter";
+    const char *mod_h2_init_key = "mod_http2_init_counter";
     nghttp2_info *ngh2;
     apr_status_t status;
     (void)plog;(void)ptemp;
@@ -91,6 +93,12 @@ static int h2_post_config(apr_pool_t *p,
                  MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
     
     switch (h2_conn_mpm_type()) {
+        case H2_MPM_SIMPLE:
+        case H2_MPM_MOTORZ:
+        case H2_MPM_NETWARE:
+        case H2_MPM_WINNT:
+            /* not sure we need something extra for those. */
+            break;
         case H2_MPM_EVENT:
         case H2_MPM_WORKER:
             /* all fine, we know these ones */
@@ -109,6 +117,9 @@ static int h2_post_config(apr_pool_t *p,
     if (status == APR_SUCCESS) {
         status = h2_switch_init(p, s);
     }
+    if (status == APR_SUCCESS) {
+        status = h2_task_init(p, s);
+    }
     
     return status;
 }
@@ -117,6 +128,31 @@ static char *http2_var_lookup(apr_pool_t
                          conn_rec *, request_rec *, char *name);
 static int http2_is_h2(conn_rec *);
 
+static apr_status_t http2_req_engine_push(const char *engine_type, 
+                                          request_rec *r, 
+                                          h2_req_engine_init *einit)
+{
+    return h2_mplx_engine_push(engine_type, r, einit);
+}
+
+static apr_status_t http2_req_engine_pull(h2_req_engine *engine, 
+                                          apr_read_type_e block, 
+                                          request_rec **pr)
+{
+    return h2_mplx_engine_pull(engine, block, pr);
+}
+
+static void http2_req_engine_done(h2_req_engine *engine, conn_rec *r_conn)
+{
+    h2_mplx_engine_done(engine, r_conn);
+}
+
+static void http2_req_engine_exit(h2_req_engine *engine)
+{
+    h2_mplx_engine_exit(engine);
+}
+
+
 /* Runs once per created child process. Perform any process 
  * related initionalization here.
  */
@@ -129,8 +165,6 @@ static void h2_child_init(apr_pool_t *po
                      APLOGNO(02949) "initializing connection handling");
     }
     
-    APR_REGISTER_OPTIONAL_FN(http2_is_h2);
-    APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
 }
 
 /* Install this module into the apache2 infrastructure.
@@ -139,6 +173,13 @@ static void h2_hooks(apr_pool_t *pool)
 {
     static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
     
+    APR_REGISTER_OPTIONAL_FN(http2_is_h2);
+    APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
+    APR_REGISTER_OPTIONAL_FN(http2_req_engine_push);
+    APR_REGISTER_OPTIONAL_FN(http2_req_engine_pull);
+    APR_REGISTER_OPTIONAL_FN(http2_req_engine_done);
+    APR_REGISTER_OPTIONAL_FN(http2_req_engine_exit);
+
     ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
     
     /* Run once after configuration is set, but before mpm children initialize.

Modified: httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/mod_http2.dsp Wed Mar  2 11:21:28 2016
@@ -43,7 +43,7 @@ RSC=rc.exe
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "ssize_t=long" /FD /c
-# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/nghttp2/lib/includes" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ssize_t=long" /Fd"Release\mod_http2_src" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/nghttp2/lib/includes" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ssize_t=long" /Fd"Release\mod_http2_src" /FD /c
 # ADD BASE MTL /nologo /D "NDEBUG" /win32
 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
@@ -75,7 +75,7 @@ PostBuild_Cmds=if exist $(TargetPath).ma
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "ssize_t=long" /FD /c
-# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/nghttp2/lib/includes" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ssize_t=long" /Fd"Debug\mod_http2_src" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../../srclib/nghttp2/lib/includes" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ssize_t=long" /Fd"Debug\mod_http2_src" /FD /c
 # ADD BASE MTL /nologo /D "_DEBUG" /win32
 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
@@ -141,6 +141,10 @@ SOURCE=./h2_h2.c
 # End Source File
 # Begin Source File
 
+SOURCE=./h2_int_queue.c
+# End Source File
+# Begin Source File
+
 SOURCE=./h2_io.c
 # End Source File
 # Begin Source File
@@ -173,10 +177,6 @@ SOURCE=./h2_stream.c
 # End Source File
 # Begin Source File
 
-SOURCE=./h2_stream_set.c
-# End Source File
-# Begin Source File
-
 SOURCE=./h2_switch.c
 # End Source File
 # Begin Source File
@@ -193,10 +193,6 @@ SOURCE=./h2_task_output.c
 # End Source File
 # Begin Source File
 
-SOURCE=./h2_task_queue.c
-# End Source File
-# Begin Source File
-
 SOURCE=./h2_util.c
 # End Source File
 # Begin Source File

Modified: httpd/httpd/branches/2.4.x/modules/http2/mod_http2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/mod_http2.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/mod_http2.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/mod_http2.h Wed Mar  2 11:21:28 2016
@@ -13,18 +13,101 @@
  * limitations under the License.
  */
 
-#ifndef mod_http2_mod_http2_h
-#define mod_http2_mod_http2_h
+#ifndef __MOD_HTTP2_H__
+#define __MOD_HTTP2_H__
 
 /** The http2_var_lookup() optional function retrieves HTTP2 environment
  * variables. */
-APR_DECLARE_OPTIONAL_FN(char *, http2_var_lookup,
-                        (apr_pool_t *, server_rec *,
-                         conn_rec *, request_rec *,
-                         char *));
+APR_DECLARE_OPTIONAL_FN(char *, 
+                        http2_var_lookup, (apr_pool_t *, server_rec *,
+                                           conn_rec *, request_rec *,  char *));
 
 /** An optional function which returns non-zero if the given connection
  * or its master connection is using HTTP/2. */
-APR_DECLARE_OPTIONAL_FN(int, http2_is_h2, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, 
+                        http2_is_h2, (conn_rec *));
+
+
+/*******************************************************************************
+ * HTTP/2 request engines
+ ******************************************************************************/
+ 
+struct apr_thread_cond_t;
+
+typedef struct h2_req_engine h2_req_engine;
+
+/**
+ * Initialize a h2_req_engine. The structure will be passed in but
+ * only the name and master are set. The function should initialize
+ * all fields.
+ * @param engine the allocated, partially filled structure
+ * @param r      the first request to process, or NULL
+ */
+typedef apr_status_t h2_req_engine_init(h2_req_engine *engine, request_rec *r);
+
+/**
+ * The public structure of a h2_req_engine. It gets allocated by the http2
+ * infrastructure, assigned id, type, pool, io and connection and passed to the
+ * h2_req_engine_init() callback to complete initialization.
+ * This happens whenever a new request gets "push"ed for an engine type and
+ * no instance, or no free instance, for the type is available.
+ */
+struct h2_req_engine {
+    const char *id;        /* identifier */
+    apr_pool_t *pool;      /* pool for engine specific allocations */
+    const char *type;      /* name of the engine type */
+    unsigned char window_bits;/* preferred size of overall response data
+                            * mod_http2 is willing to buffer as log2 */
+    unsigned char req_window_bits;/* preferred size of response body data
+                            * mod_http2 is willing to buffer per request,
+                            * as log2 */
+    apr_size_t capacity;   /* maximum concurrent requests */
+    void *user_data;       /* user specific data */
+};
+
+/**
+ * Push a request to an engine with the specified name for further processing.
+ * If no such engine is available, einit is not NULL, einit is called 
+ * with a new engine record and the caller is responsible for running the
+ * new engine instance.
+ * @param engine_type the type of the engine to add the request to
+ * @param r           the request to push to an engine for processing
+ * @param einit       an optional initialization callback for a new engine 
+ *                    of the requested type, should no instance be available.
+ *                    By passing a non-NULL callback, the caller is willing
+ *                    to init and run a new engine itself.
+ * @return APR_SUCCESS iff slave was successfully added to an engine
+ */
+APR_DECLARE_OPTIONAL_FN(apr_status_t, 
+                        http2_req_engine_push, (const char *engine_type, 
+                                                request_rec *r,
+                                                h2_req_engine_init *einit));
+
+/**
+ * Get a new request for processing in this engine.
+ * @param engine      the engine which is done processing the slave
+ * @param timeout     wait a maximum amount of time for a new slave, 0 will not wait
+ * @param pslave      the slave connection that needs processing or NULL
+ * @return APR_SUCCESS if new request was assigned
+ *         APR_EAGAIN/APR_TIMEUP if no new request is available
+ *         APR_ECONNABORTED if the engine needs to shut down
+ */
+APR_DECLARE_OPTIONAL_FN(apr_status_t, 
+                        http2_req_engine_pull, (h2_req_engine *engine, 
+                                                apr_read_type_e block,
+                                                request_rec **pr));
+APR_DECLARE_OPTIONAL_FN(void, 
+                        http2_req_engine_done, (h2_req_engine *engine, 
+                                                conn_rec *rconn));
+/**
+ * The given request engine is done processing and needs to be excluded
+ * from further handling. 
+ * @param engine      the engine to exit
+ */
+APR_DECLARE_OPTIONAL_FN(void,
+                        http2_req_engine_exit, (h2_req_engine *engine));
+
+
+#define H2_TASK_ID_NOTE     "http2-task-id"
 
 #endif