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 2022/03/18 09:52:52 UTC

svn commit: r1899032 - in /httpd/httpd/trunk: ./ build/ changes-entries/ include/ modules/http2/ server/ test/modules/http2/

Author: icing
Date: Fri Mar 18 09:52:52 2022
New Revision: 1899032

URL: http://svn.apache.org/viewvc?rev=1899032&view=rev
Log:
  *) core: adding a new hook and method to the API:
     create_secondary_connection and ap_create_secondary_connection()
     to setup connections related to a "master" one, as used in
     the HTTP/2 protocol implementation.

  *) mod_http2: using the new API calls to get rid of knowledge
     about how the core handles conn_rec specifics.
     Improvements in pollset stream handling to use less sets.
     Using atomic read/writes instead of volatiles now.
     Keeping a reserve of "transit" pools and bucket_allocs for
     use on secondary connections to avoid repeated setup/teardowns.


Added:
    httpd/httpd/trunk/changes-entries/core_secondary_conn.txt
Modified:
    httpd/httpd/trunk/   (props changed)
    httpd/httpd/trunk/build/   (props changed)
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/include/http_connection.h
    httpd/httpd/trunk/modules/http2/h2_c2.c
    httpd/httpd/trunk/modules/http2/h2_c2.h
    httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
    httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_workers.c
    httpd/httpd/trunk/modules/http2/h2_workers.h
    httpd/httpd/trunk/server/connection.c
    httpd/httpd/trunk/server/core.c
    httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py

Propchange: httpd/httpd/trunk/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Mar 18 09:52:52 2022
@@ -49,3 +49,5 @@ TAGS
 .project
 .idea
 .pytest_cache
+check
+clog

Propchange: httpd/httpd/trunk/build/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Mar 18 09:52:52 2022
@@ -9,3 +9,4 @@ PrintPath
 config.sub
 config.guess
 config_vars.sh
+config_vars.out

Added: httpd/httpd/trunk/changes-entries/core_secondary_conn.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/core_secondary_conn.txt?rev=1899032&view=auto
==============================================================================
--- httpd/httpd/trunk/changes-entries/core_secondary_conn.txt (added)
+++ httpd/httpd/trunk/changes-entries/core_secondary_conn.txt Fri Mar 18 09:52:52 2022
@@ -0,0 +1,11 @@
+  *) core: adding a new hook and method to the API:
+     create_secondary_connection and ap_create_secondary_connection()
+     to setup connections related to a "master" one, as used in
+     the HTTP/2 protocol implementation.
+
+  *) mod_http2: using the new API calls to get rid of knowledge
+     about how the core handles conn_rec specifics.
+     Improvements in pollset stream handling to use less sets.
+     Using atomic read/writes instead of volatiles now.
+     Keeping a reserve of "transit" pools and bucket_allocs for
+     use on secondary connections to avoid repeated setup/teardowns.

Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Fri Mar 18 09:52:52 2022
@@ -702,6 +702,9 @@
  *                         and ap_thread_current()
  * 20211221.4 (2.5.1-dev)  Add hook child_stopped to get informed that a child
  *                         has stopped processing any requests.
+ * 20211221.5 (2.5.1-dev)  Add hook create_secondary_connection and method
+ *                         ap_create_secondary_connection() to have connection
+ *                         setup of http2-like connections in core.
  *
  */
 
@@ -710,7 +713,7 @@
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20211221
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 4             /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 5             /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

Modified: httpd/httpd/trunk/include/http_connection.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/http_connection.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/include/http_connection.h (original)
+++ httpd/httpd/trunk/include/http_connection.h Fri Mar 18 09:52:52 2022
@@ -151,6 +151,20 @@ AP_DECLARE_HOOK(int,pre_close_connection
 AP_DECLARE(int) ap_pre_connection(conn_rec *c, void *csd);
 
 /**
+ * create_secondary_connection is a RUN_FIRST hook which allows modules to create
+ * secondary connections. In general, you should not install filters with the
+ * create_secondary_connection hook. This hook should close the connection
+ * if it encounters a fatal error condition.
+ *
+ * @param p The pool for the secondary connection
+ * @param master The master connection this belongs to.
+ * @param alloc The bucket allocator to use for all bucket/brigade creations
+ * @return An allocated connection record or NULL.
+ */
+AP_DECLARE_HOOK(conn_rec *, create_secondary_connection,
+                (apr_pool_t *p, conn_rec *master, apr_bucket_alloc_t *alloc))
+
+/**
  * Create a new server/incoming or client/outgoing/proxy connection
  * @param p The pool from which to allocate the connection record
  * @param server The server record to create the connection too.
@@ -171,6 +185,17 @@ AP_DECLARE(conn_rec *) ap_create_connect
                                             apr_bucket_alloc_t *alloc,
                                             unsigned int outgoing);
 
+/**
+ * Create a new secondary connection based on a master one.
+ * @param pool  The pool for the secondary connection
+ * @param master The master connection this belongs to.
+ * @param alloc The bucket allocator to use for all bucket/brigade creations
+ * @return An allocated connection record or NULL.
+ */
+AP_DECLARE(conn_rec *) ap_create_secondary_connection(apr_pool_t *pool,
+                                                      conn_rec *master,
+                                                      apr_bucket_alloc_t *alloc);
+
 
 /** End Of Connection (EOC) bucket */
 AP_DECLARE_DATA extern const apr_bucket_type_t ap_bucket_type_eoc;

Modified: httpd/httpd/trunk/modules/http2/h2_c2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2.c Fri Mar 18 09:52:52 2022
@@ -131,12 +131,6 @@ int h2_mpm_supported(void)
     return mpm_supported;
 }
 
-static module *h2_conn_mpm_module(void)
-{
-    check_modules(0);
-    return mpm_module;
-}
-
 apr_status_t h2_c2_child_init(apr_pool_t *pool, server_rec *s)
 {
     check_modules(1);
@@ -144,88 +138,6 @@ apr_status_t h2_c2_child_init(apr_pool_t
                              APR_PROTO_TCP, pool);
 }
 
-/* APR callback invoked if allocation fails. */
-static int abort_on_oom(int retcode)
-{
-    ap_abort_on_oom();
-    return retcode; /* unreachable, hopefully. */
-}
-
-conn_rec *h2_c2_create(conn_rec *c1, apr_pool_t *parent)
-{
-    apr_allocator_t *allocator;
-    apr_status_t status;
-    apr_pool_t *pool;
-    conn_rec *c2;
-    void *cfg;
-    module *mpm;
-
-    ap_assert(c1);
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c1,
-                  "h2_c2: create for c1(%ld)", c1->id);
-
-    /* We create a pool with its own allocator to be used for
-     * processing a request. This is the only way to have the processing
-     * independent of its parent pool in the sense that it can work in
-     * another thread.
-     */
-    apr_allocator_create(&allocator);
-    apr_allocator_max_free_set(allocator, ap_max_mem_free);
-    status = apr_pool_create_ex(&pool, parent, NULL, allocator);
-    if (status != APR_SUCCESS) {
-        ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c1,
-                      APLOGNO(10004) "h2_c2: create pool");
-        return NULL;
-    }
-    apr_allocator_owner_set(allocator, pool);
-    apr_pool_abort_set(abort_on_oom, pool);
-    apr_pool_tag(pool, "h2_c2_conn");
-
-    c2 = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
-    memcpy(c2, c1, sizeof(conn_rec));
-
-    c2->master                 = c1;
-    c2->pool                   = pool;
-    c2->conn_config            = ap_create_conn_config(pool);
-    c2->notes                  = apr_table_make(pool, 5);
-    c2->input_filters          = NULL;
-    c2->output_filters         = NULL;
-    c2->keepalives             = 0;
-#if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
-    c2->filter_conn_ctx        = NULL;
-#endif
-    c2->bucket_alloc           = apr_bucket_alloc_create(pool);
-#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
-    c2->data_in_input_filters  = 0;
-    c2->data_in_output_filters = 0;
-#endif
-    /* prevent mpm_event from making wrong assumptions about this connection,
-     * like e.g. using its socket for an async read check. */
-    c2->clogging_input_filters = 1;
-    c2->log                    = NULL;
-    c2->aborted                = 0;
-    /* We cannot install the master connection socket on the secondary, as
-     * modules mess with timeouts/blocking of the socket, with
-     * unwanted side effects to the master connection processing.
-     * Fortunately, since we never use the secondary socket, we can just install
-     * a single, process-wide dummy and everyone is happy.
-     */
-    ap_set_module_config(c2->conn_config, &core_module, dummy_socket);
-    /* TODO: these should be unique to this thread */
-    c2->sbh = NULL; /*c1->sbh;*/
-    /* TODO: not all mpm modules have learned about secondary connections yet.
-     * copy their config from master to secondary.
-     */
-    if ((mpm = h2_conn_mpm_module()) != NULL) {
-        cfg = ap_get_module_config(c1->conn_config, mpm);
-        ap_set_module_config(c2->conn_config, mpm, cfg);
-    }
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
-                  "h2_c2(%s): created", c2->log_id);
-    return c2;
-}
-
 void h2_c2_destroy(conn_rec *c2)
 {
     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c2,
@@ -233,6 +145,21 @@ void h2_c2_destroy(conn_rec *c2)
     apr_pool_destroy(c2->pool);
 }
 
+void h2_c2_abort(conn_rec *c2, conn_rec *from)
+{
+    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);
+
+    AP_DEBUG_ASSERT(conn_ctx);
+    AP_DEBUG_ASSERT(conn_ctx->stream_id);
+    if (conn_ctx->beam_in) {
+        h2_beam_abort(conn_ctx->beam_in, from);
+    }
+    if (conn_ctx->beam_out) {
+        h2_beam_abort(conn_ctx->beam_out, from);
+    }
+    c2->aborted = 1;
+}
+
 typedef struct {
     apr_bucket_brigade *bb;       /* c2: data in holding area */
 } h2_c2_fctx_in_t;
@@ -458,10 +385,7 @@ static apr_status_t h2_c2_filter_out(ap_
                   "h2_c2(%s-%d): output leave",
                   conn_ctx->id, conn_ctx->stream_id);
     if (APR_SUCCESS != rv) {
-        if (!conn_ctx->done) {
-            h2_beam_abort(conn_ctx->beam_out, f->c);
-        }
-        f->c->aborted = 1;
+        h2_c2_abort(f->c, f->c);
     }
     return rv;
 }

Modified: httpd/httpd/trunk/modules/http2/h2_c2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_c2.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_c2.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_c2.h Fri Mar 18 09:52:52 2022
@@ -41,10 +41,17 @@ int h2_mpm_supported(void);
  */
 apr_status_t h2_c2_child_init(apr_pool_t *pool, server_rec *s);
 
-conn_rec *h2_c2_create(conn_rec *c1, apr_pool_t *parent);
 void h2_c2_destroy(conn_rec *c2);
 
 /**
+ * Abort the I/O processing of a secondary connection. And
+ * in-/output beams will return errors and c2->aborted is set.
+ * @param c2 the secondary connection to abort
+ * @param from the connection this is invoked from
+ */
+void h2_c2_abort(conn_rec *c2, conn_rec *from);
+
+/**
  * Process a secondary connection for a HTTP/2 stream request.
  */
 apr_status_t h2_c2_process(conn_rec *c, apr_thread_t *thread, int worker_id);

Modified: httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_ctx.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_ctx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn_ctx.c Fri Mar 18 09:52:52 2022
@@ -16,6 +16,7 @@
  
 #include <assert.h>
 #include <apr_strings.h>
+#include <apr_atomic.h>
 
 #include <httpd.h>
 #include <http_core.h>
@@ -42,6 +43,7 @@ static h2_conn_ctx_t *ctx_create(conn_re
     h2_conn_ctx_t *conn_ctx = apr_pcalloc(c->pool, sizeof(*conn_ctx));
     conn_ctx->id = id;
     conn_ctx->server = c->base_server;
+    apr_atomic_set32(&conn_ctx->started, 1);
     conn_ctx->started_at = apr_time_now();
 
     ap_set_module_config(c->conn_config, &http2_module, conn_ctx);
@@ -66,7 +68,8 @@ h2_conn_ctx_t *h2_conn_ctx_create_for_c1
 }
 
 apr_status_t h2_conn_ctx_init_for_c2(h2_conn_ctx_t **pctx, conn_rec *c2,
-                                     struct h2_mplx *mplx, struct h2_stream *stream)
+                                     struct h2_mplx *mplx, struct h2_stream *stream,
+                                     struct h2_c2_transit *transit)
 {
     h2_conn_ctx_t *conn_ctx;
     apr_status_t rv = APR_SUCCESS;
@@ -85,10 +88,12 @@ apr_status_t h2_conn_ctx_init_for_c2(h2_
     }
 
     conn_ctx->mplx = mplx;
+    conn_ctx->transit = transit;
     conn_ctx->stream_id = stream->id;
     apr_pool_create(&conn_ctx->req_pool, c2->pool);
     apr_pool_tag(conn_ctx->req_pool, "H2_C2_REQ");
     conn_ctx->request = stream->request;
+    apr_atomic_set32(&conn_ctx->started, 1);
     conn_ctx->started_at = apr_time_now();
     conn_ctx->done = 0;
     conn_ctx->done_at = 0;
@@ -97,38 +102,6 @@ apr_status_t h2_conn_ctx_init_for_c2(h2_
     return rv;
 }
 
-void h2_conn_ctx_clear_for_c2(conn_rec *c2)
-{
-    h2_conn_ctx_t *conn_ctx;
-
-    ap_assert(c2->master);
-    conn_ctx = h2_conn_ctx_get(c2);
-    conn_ctx->stream_id = -1;
-    conn_ctx->request = NULL;
-
-    if (conn_ctx->req_pool) {
-        apr_pool_destroy(conn_ctx->req_pool);
-        conn_ctx->req_pool = NULL;
-        conn_ctx->beam_out = NULL;
-    }
-    memset(&conn_ctx->pfd_in_drain, 0, sizeof(conn_ctx->pfd_in_drain));
-    memset(&conn_ctx->pfd_out_prod, 0, sizeof(conn_ctx->pfd_out_prod));
-    conn_ctx->beam_in = NULL;
-}
-
-void h2_conn_ctx_destroy(conn_rec *c)
-{
-    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
-
-    if (conn_ctx) {
-        if (conn_ctx->mplx_pool) {
-            apr_pool_destroy(conn_ctx->mplx_pool);
-            conn_ctx->mplx_pool = NULL;
-        }
-        ap_set_module_config(c->conn_config, &http2_module, NULL);
-    }
-}
-
 void h2_conn_ctx_set_timeout(h2_conn_ctx_t *conn_ctx, apr_interval_time_t timeout)
 {
     if (conn_ctx->beam_out) {

Modified: httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_ctx.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_ctx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn_ctx.h Fri Mar 18 09:52:52 2022
@@ -22,6 +22,7 @@ struct h2_stream;
 struct h2_mplx;
 struct h2_bucket_beam;
 struct h2_response_parser;
+struct h2_c2_transit;
 
 #define H2_PIPE_OUT     0
 #define H2_PIPE_IN      1
@@ -40,6 +41,7 @@ struct h2_conn_ctx_t {
     const char *protocol;           /* c1: the protocol negotiated */
     struct h2_session *session;     /* c1: the h2 session established */
     struct h2_mplx *mplx;           /* c2: the multiplexer */
+    struct h2_c2_transit *transit;  /* c2: transit pool and bucket_alloc */
 
     int pre_conn_done;               /* has pre_connection setup run? */
     int stream_id;                  /* c1: 0, c2: stream id processed */
@@ -48,20 +50,17 @@ struct h2_conn_ctx_t {
     struct h2_bucket_beam *beam_out; /* c2: data out, created from req_pool */
     struct h2_bucket_beam *beam_in;  /* c2: data in or NULL, borrowed from request stream */
 
-    apr_pool_t *mplx_pool;           /* c2: an mplx child pool for safe use inside mplx lock */
     apr_file_t *pipe_in_prod[2];     /* c2: input produced notification pipe */
-    apr_file_t *pipe_in_drain[2];    /* c2: input drained notification pipe */
     apr_file_t *pipe_out_prod[2];    /* c2: output produced notification pipe */
 
-    apr_pollfd_t pfd_in_drain;       /* c2: poll pipe_in_drain output */
     apr_pollfd_t pfd_out_prod;       /* c2: poll pipe_out_prod output */
 
     int has_final_response;          /* final HTTP response passed on out */
     apr_status_t last_err;           /* APR_SUCCES or last error encountered in filters */
-    struct h2_response_parser *parser; /* optional parser to catch H1 responses */
 
-    volatile int done;               /* c2: processing has finished */
+    /* atomic */ apr_uint32_t started; /* c2: processing was started */
     apr_time_t started_at;           /* c2: when processing started */
+    /* atomic */ apr_uint32_t done;  /* c2: processing has finished */
     apr_time_t done_at;              /* c2: when processing was done */
 };
 typedef struct h2_conn_ctx_t h2_conn_ctx_t;
@@ -84,14 +83,11 @@ typedef struct h2_conn_ctx_t h2_conn_ctx
 h2_conn_ctx_t *h2_conn_ctx_create_for_c1(conn_rec *c, server_rec *s, const char *protocol);
 
 apr_status_t h2_conn_ctx_init_for_c2(h2_conn_ctx_t **pctx, conn_rec *c,
-                                     struct h2_mplx *mplx, struct h2_stream *stream);
-
-void h2_conn_ctx_clear_for_c2(conn_rec *c2);
+                                     struct h2_mplx *mplx, struct h2_stream *stream,
+                                     struct h2_c2_transit *transit);
 
 void h2_conn_ctx_detach(conn_rec *c);
 
-void h2_conn_ctx_destroy(conn_rec *c);
-
 void h2_conn_ctx_set_timeout(h2_conn_ctx_t *conn_ctx, apr_interval_time_t timeout);
 
 #endif /* defined(__mod_h2__h2_conn_ctx__) */

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Fri Mar 18 09:52:52 2022
@@ -26,6 +26,7 @@
 
 #include <httpd.h>
 #include <http_core.h>
+#include <http_connection.h>
 #include <http_log.h>
 
 #include <mpm_common.h>
@@ -69,6 +70,13 @@ static apr_status_t mplx_pollset_poll(h2
 
 static apr_pool_t *pchild;
 
+/* APR callback invoked if allocation fails. */
+static int abort_on_oom(int retcode)
+{
+    ap_abort_on_oom();
+    return retcode; /* unreachable, hopefully. */
+}
+
 apr_status_t h2_mplx_c1_child_init(apr_pool_t *pool, server_rec *s)
 {
     pchild = pool;
@@ -100,7 +108,8 @@ static void c1_input_consumed(void *ctx,
 static int stream_is_running(h2_stream *stream)
 {
     h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(stream->c2);
-    return conn_ctx && conn_ctx->started_at != 0 && !conn_ctx->done;
+    return conn_ctx && apr_atomic_read32(&conn_ctx->started) != 0
+        && apr_atomic_read32(&conn_ctx->done) == 0;
 }
 
 int h2_mplx_c1_stream_is_running(h2_mplx *m, h2_stream *stream)
@@ -153,13 +162,7 @@ static void m_stream_cleanup(h2_mplx *m,
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c1,
                           H2_STRM_MSG(stream, "cleanup, c2 is running, abort"));
             /* c2 is still running */
-            stream->c2->aborted = 1;
-            if (stream->input) {
-                h2_beam_abort(stream->input, m->c1);
-            }
-            if (stream->output) {
-                h2_beam_abort(stream->output, m->c1);
-            }
+            h2_c2_abort(stream->c2, m->c1);
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c1,
                           H2_STRM_MSG(stream, "cleanup, c2 is done, move to shold"));
             h2_ihash_add(m->shold, stream);
@@ -173,6 +176,66 @@ static void m_stream_cleanup(h2_mplx *m,
     }
 }
 
+static h2_c2_transit *c2_transit_create(h2_mplx *m)
+{
+    apr_allocator_t *allocator;
+    apr_pool_t *ptrans;
+    h2_c2_transit *transit;
+    apr_status_t rv;
+
+    /* We create a pool with its own allocator to be used for
+     * processing a request. This is the only way to have the processing
+     * independent of its parent pool in the sense that it can work in
+     * another thread.
+     */
+
+    rv = apr_allocator_create(&allocator);
+    if (rv == APR_SUCCESS) {
+        apr_allocator_max_free_set(allocator, ap_max_mem_free);
+        rv = apr_pool_create_ex(&ptrans, m->pool, NULL, allocator);
+    }
+    if (rv != APR_SUCCESS) {
+        /* maybe the log goes through, maybe not. */
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, m->c1,
+                      APLOGNO(10004) "h2_mplx: create transit pool");
+        ap_abort_on_oom();
+        return NULL; /* should never be reached. */
+    }
+
+    apr_allocator_owner_set(allocator, ptrans);
+    apr_pool_abort_set(abort_on_oom, ptrans);
+    apr_pool_tag(ptrans, "h2_c2_transit");
+
+    transit = apr_pcalloc(ptrans, sizeof(*transit));
+    transit->pool = ptrans;
+    transit->bucket_alloc = apr_bucket_alloc_create(ptrans);
+    return transit;
+}
+
+static void c2_transit_destroy(h2_c2_transit *transit)
+{
+    apr_pool_destroy(transit->pool);
+}
+
+static h2_c2_transit *c2_transit_get(h2_mplx *m)
+{
+    h2_c2_transit **ptransit = apr_array_pop(m->c2_transits);
+    if (ptransit) {
+        return *ptransit;
+    }
+    return c2_transit_create(m);
+}
+
+static void c2_transit_recycle(h2_mplx *m, h2_c2_transit *transit)
+{
+    if (m->c2_transits->nelts >= m->max_spare_transits) {
+        c2_transit_destroy(transit);
+    }
+    else {
+        APR_ARRAY_PUSH(m->c2_transits, h2_c2_transit*) = transit;
+    }
+}
+
 /**
  * A h2_mplx needs to be thread-safe *and* if will be called by
  * the h2_session thread *and* the h2_worker threads. Therefore:
@@ -254,11 +317,11 @@ h2_mplx *h2_mplx_c1_create(h2_stream *st
     m->streams_ev_in = apr_array_make(m->pool, 10, sizeof(h2_stream*));
     m->streams_ev_out = apr_array_make(m->pool, 10, sizeof(h2_stream*));
 
-#if !H2_POLL_STREAMS
+    m->streams_input_read = h2_iq_create(m->pool, 10);
     status = apr_thread_mutex_create(&m->poll_lock, APR_THREAD_MUTEX_DEFAULT,
                                      m->pool);
     if (APR_SUCCESS != status) goto failure;
-    m->streams_input_read = h2_iq_create(m->pool, 10);
+#if !H2_POLL_STREAMS
     m->streams_output_written = h2_iq_create(m->pool, 10);
 #endif
 
@@ -266,6 +329,8 @@ h2_mplx *h2_mplx_c1_create(h2_stream *st
     mplx_pollset_add(m, conn_ctx);
 
     m->scratch_r = apr_pcalloc(m->pool, sizeof(*m->scratch_r));
+    m->max_spare_transits = 3;
+    m->c2_transits = apr_array_make(m->pool, m->max_spare_transits, sizeof(h2_c2_transit*));
 
     return m;
 
@@ -331,8 +396,9 @@ static int m_report_stream_iter(void *ct
                       H2_STRM_MSG(stream, "->03198: %s %s %s"
                       "[started=%d/done=%d]"), 
                       conn_ctx->request->method, conn_ctx->request->authority,
-                      conn_ctx->request->path, conn_ctx->started_at != 0,
-                      conn_ctx->done);
+                      conn_ctx->request->path,
+                      (int)apr_atomic_read32(&conn_ctx->started),
+                      (int)apr_atomic_read32(&conn_ctx->done));
     }
     else {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c1, /* NO APLOGNO */
@@ -354,10 +420,6 @@ static int m_stream_cancel_iter(void *ct
     h2_mplx *m = ctx;
     h2_stream *stream = val;
 
-    /* disable input consumed reporting */
-    if (stream->input) {
-        h2_beam_abort(stream->input, m->c1);
-    }
     /* take over event monitoring */
     h2_stream_set_monitor(stream, NULL);
     /* Reset, should transit to CLOSED state */
@@ -499,8 +561,11 @@ static void c1_purge_streams(h2_mplx *m)
                               m->id, stream->id, c2_ctx->stream_id);
             }
 
-            h2_conn_ctx_destroy(c2);
             h2_c2_destroy(c2);
+            if (c2_ctx->transit) {
+                c2_transit_recycle(m, c2_ctx->transit);
+                c2_ctx->transit = NULL;
+            }
         }
         h2_stream_destroy(stream);
     }
@@ -699,17 +764,10 @@ static void c2_beam_input_read_notify(vo
     h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
 
     if (conn_ctx && conn_ctx->stream_id) {
-        if (conn_ctx->pipe_in_drain[H2_PIPE_IN]) {
-            apr_file_putc(1, conn_ctx->pipe_in_drain[H2_PIPE_IN]);
-        }
-#if !H2_POLL_STREAMS
-        else {
-            apr_thread_mutex_lock(conn_ctx->mplx->poll_lock);
-            h2_iq_append(conn_ctx->mplx->streams_input_read, conn_ctx->stream_id);
-            apr_pollset_wakeup(conn_ctx->mplx->pollset);
-            apr_thread_mutex_unlock(conn_ctx->mplx->poll_lock);
-        }
-#endif
+        apr_thread_mutex_lock(conn_ctx->mplx->poll_lock);
+        h2_iq_append(conn_ctx->mplx->streams_input_read, conn_ctx->stream_id);
+        apr_pollset_wakeup(conn_ctx->mplx->pollset);
+        apr_thread_mutex_unlock(conn_ctx->mplx->poll_lock);
     }
 }
 
@@ -733,13 +791,13 @@ static void c2_beam_output_write_notify(
     }
 }
 
-static apr_status_t c2_setup_io(h2_mplx *m, conn_rec *c2, h2_stream *stream)
+static apr_status_t c2_setup_io(h2_mplx *m, conn_rec *c2, h2_stream *stream, h2_c2_transit *transit)
 {
     h2_conn_ctx_t *conn_ctx;
     apr_status_t rv = APR_SUCCESS;
     const char *action = "init";
 
-    rv = h2_conn_ctx_init_for_c2(&conn_ctx, c2, m, stream);
+    rv = h2_conn_ctx_init_for_c2(&conn_ctx, c2, m, stream, transit);
     if (APR_SUCCESS != rv) goto cleanup;
 
     if (!conn_ctx->beam_out) {
@@ -758,22 +816,14 @@ static apr_status_t c2_setup_io(h2_mplx
         h2_beam_on_received(stream->input, c2_beam_input_read_notify, c2);
         h2_beam_on_consumed(stream->input, c1_input_consumed, stream);
     }
-    else {
-        memset(&conn_ctx->pfd_in_drain, 0, sizeof(conn_ctx->pfd_in_drain));
-    }
 
 #if H2_POLL_STREAMS
-    if (!conn_ctx->mplx_pool) {
-        apr_pool_create(&conn_ctx->mplx_pool, m->pool);
-        apr_pool_tag(conn_ctx->mplx_pool, "H2_MPLX_C2");
-    }
-
     if (!conn_ctx->pipe_out_prod[H2_PIPE_OUT]) {
         action = "create output pipe";
         rv = apr_file_pipe_create_pools(&conn_ctx->pipe_out_prod[H2_PIPE_OUT],
                                         &conn_ctx->pipe_out_prod[H2_PIPE_IN],
                                         APR_FULL_NONBLOCK,
-                                        conn_ctx->mplx_pool, c2->pool);
+                                        c2->pool, c2->pool);
         if (APR_SUCCESS != rv) goto cleanup;
     }
     conn_ctx->pfd_out_prod.desc_type = APR_POLL_FILE;
@@ -787,26 +837,13 @@ static apr_status_t c2_setup_io(h2_mplx
             rv = apr_file_pipe_create_pools(&conn_ctx->pipe_in_prod[H2_PIPE_OUT],
                                             &conn_ctx->pipe_in_prod[H2_PIPE_IN],
                                             APR_READ_BLOCK,
-                                            c2->pool, conn_ctx->mplx_pool);
-            if (APR_SUCCESS != rv) goto cleanup;
-        }
-        if (!conn_ctx->pipe_in_drain[H2_PIPE_OUT]) {
-            action = "create input read pipe";
-            rv = apr_file_pipe_create_pools(&conn_ctx->pipe_in_drain[H2_PIPE_OUT],
-                                            &conn_ctx->pipe_in_drain[H2_PIPE_IN],
-                                            APR_FULL_NONBLOCK,
-                                            c2->pool, conn_ctx->mplx_pool);
+                                            c2->pool, c2->pool);
             if (APR_SUCCESS != rv) goto cleanup;
         }
-        conn_ctx->pfd_in_drain.desc_type = APR_POLL_FILE;
-        conn_ctx->pfd_in_drain.desc.f = conn_ctx->pipe_in_drain[H2_PIPE_OUT];
-        conn_ctx->pfd_in_drain.reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
-        conn_ctx->pfd_in_drain.client_data = conn_ctx;
     }
 #else
     memset(&conn_ctx->pfd_out_prod, 0, sizeof(conn_ctx->pfd_out_prod));
     memset(&conn_ctx->pipe_in_prod, 0, sizeof(conn_ctx->pipe_in_prod));
-    memset(&conn_ctx->pipe_in_drain, 0, sizeof(conn_ctx->pipe_in_drain));
 #endif
 
 cleanup:
@@ -822,9 +859,10 @@ cleanup:
 static conn_rec *s_next_c2(h2_mplx *m)
 {
     h2_stream *stream = NULL;
-    apr_status_t rv;
+    apr_status_t rv = APR_SUCCESS;
     int sid;
-    conn_rec *c2;
+    conn_rec *c2 = NULL;
+    h2_c2_transit *transit = NULL;
 
     while (!m->aborted && !stream && (m->processing_count < m->processing_limit)
            && (sid = h2_iq_shift(m->q)) > 0) {
@@ -838,27 +876,35 @@ static conn_rec *s_next_c2(h2_mplx *m)
                           "Current limit is %d and %d workers are in use.",
                           m->id, m->processing_limit, m->processing_count);
         }
-        return NULL;
+        goto cleanup;
     }
 
     if (sid > m->max_stream_id_started) {
         m->max_stream_id_started = sid;
     }
 
-    c2 = h2_c2_create(m->c1, m->pool);
+    transit = c2_transit_get(m);
+    c2 = ap_create_secondary_connection(transit->pool, m->c1, transit->bucket_alloc);
+    if (!c2) goto cleanup;
     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, m->c1,
                   H2_STRM_MSG(stream, "created new c2"));
 
-    rv = c2_setup_io(m, c2, stream);
-    if (APR_SUCCESS != rv) {
-        return NULL;
-    }
+    rv = c2_setup_io(m, c2, stream, transit);
+    if (APR_SUCCESS != rv) goto cleanup;
 
     stream->c2 = c2;
     ++m->processing_count;
     APR_ARRAY_PUSH(m->streams_to_poll, h2_stream *) = stream;
     apr_pollset_wakeup(m->pollset);
 
+cleanup:
+    if (APR_SUCCESS != rv && c2) {
+        h2_c2_destroy(c2);
+        c2 = NULL;
+    }
+    if (transit && !c2) {
+        c2_transit_recycle(m, transit);
+    }
     return c2;
 }
 
@@ -896,8 +942,8 @@ static void s_c2_done(h2_mplx *m, conn_r
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c2,
                   "h2_mplx(%s-%d): c2 done", conn_ctx->id, conn_ctx->stream_id);
 
-    ap_assert(conn_ctx->done == 0);
-    conn_ctx->done = 1;
+    AP_DEBUG_ASSERT(apr_atomic_read32(&conn_ctx->done) == 0);
+    apr_atomic_set32(&conn_ctx->done, 1);
     conn_ctx->done_at = apr_time_now();
     ++c2->keepalives;
     /* From here on, the final handling of c2 is done by c1 processing.
@@ -955,16 +1001,18 @@ static void s_c2_done(h2_mplx *m, conn_r
 void h2_mplx_worker_c2_done(conn_rec *c2, conn_rec **out_c2)
 {
     h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c2);
-    h2_mplx *m;
+    h2_mplx *m = conn_ctx? conn_ctx->mplx : NULL;
 
-    if (!conn_ctx || !conn_ctx->mplx) return;
-    m = conn_ctx->mplx;
+    if (!m) {
+        if (out_c2) *out_c2 = NULL;
+        return;
+    }
 
     H2_MPLX_ENTER_ALWAYS(m);
 
     --m->processing_count;
     s_c2_done(m, c2, conn_ctx);
-    
+
     if (m->join_wait) {
         apr_thread_cond_signal(m->join_wait);
     }
@@ -1084,52 +1132,19 @@ static apr_status_t mplx_pollset_create(
 
 static apr_status_t mplx_pollset_add(h2_mplx *m, h2_conn_ctx_t *conn_ctx)
 {
-    apr_status_t rv = APR_SUCCESS;
-    const char *name = "";
-
     if (conn_ctx->pfd_out_prod.reqevents) {
-        name = "adding out";
-        rv = apr_pollset_add(m->pollset, &conn_ctx->pfd_out_prod);
-        if (APR_SUCCESS != rv) goto cleanup;
-    }
-
-    if (conn_ctx->pfd_in_drain.reqevents) {
-        name = "adding in_read";
-        rv = apr_pollset_add(m->pollset, &conn_ctx->pfd_in_drain);
-    }
-
-cleanup:
-    if (APR_SUCCESS != rv) {
-        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, m->c1,
-                      "h2_mplx(%ld-%d): error while adding to pollset %s",
-                      m->id, conn_ctx->stream_id, name);
+        return apr_pollset_add(m->pollset, &conn_ctx->pfd_out_prod);
     }
-    return rv;
+    return APR_SUCCESS;
 }
 
 static apr_status_t mplx_pollset_remove(h2_mplx *m, h2_conn_ctx_t *conn_ctx)
 {
     apr_status_t rv = APR_SUCCESS;
-    const char *name = "";
 
     if (conn_ctx->pfd_out_prod.reqevents) {
         rv = apr_pollset_remove(m->pollset, &conn_ctx->pfd_out_prod);
         conn_ctx->pfd_out_prod.reqevents = 0;
-        if (APR_SUCCESS != rv) goto cleanup;
-    }
-
-    if (conn_ctx->pfd_in_drain.reqevents) {
-        name = "in_read";
-        rv = apr_pollset_remove(m->pollset, &conn_ctx->pfd_in_drain);
-        conn_ctx->pfd_in_drain.reqevents = 0;
-        if (APR_SUCCESS != rv) goto cleanup;
-    }
-
-cleanup:
-    if (APR_SUCCESS != rv) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, m->c1,
-                      "h2_mplx(%ld-%d): error removing from pollset %s",
-                      m->id, conn_ctx->stream_id, name);
     }
     return rv;
 }
@@ -1168,16 +1183,21 @@ static apr_status_t mplx_pollset_poll(h2
                 apr_array_clear(m->streams_to_poll);
             }
 
-#if !H2_POLL_STREAMS
             apr_thread_mutex_lock(m->poll_lock);
-            if (!h2_iq_empty(m->streams_input_read)
-                || !h2_iq_empty(m->streams_output_written)) {
+            if (!h2_iq_empty(m->streams_input_read)) {
                 while ((i = h2_iq_shift(m->streams_input_read))) {
                     stream = h2_ihash_get(m->streams, i);
                     if (stream) {
                         APR_ARRAY_PUSH(m->streams_ev_in, h2_stream*) = stream;
                     }
                 }
+                nresults = 0;
+                rv = APR_SUCCESS;
+                apr_thread_mutex_unlock(m->poll_lock);
+                break;
+            }
+#if !H2_POLL_STREAMS
+            if (!h2_iq_empty(m->streams_output_written)) {
                 while ((i = h2_iq_shift(m->streams_output_written))) {
                     stream = h2_ihash_get(m->streams, i);
                     if (stream) {
@@ -1189,8 +1209,9 @@ static apr_status_t mplx_pollset_poll(h2
                 apr_thread_mutex_unlock(m->poll_lock);
                 break;
             }
-            apr_thread_mutex_unlock(m->poll_lock);
 #endif
+            apr_thread_mutex_unlock(m->poll_lock);
+
             H2_MPLX_LEAVE(m);
             rv = apr_pollset_poll(m->pollset, timeout >= 0? timeout : -1, &nresults, &results);
             H2_MPLX_ENTER_ALWAYS(m);
@@ -1276,14 +1297,6 @@ static apr_status_t mplx_pollset_poll(h2
                               pfd->rtnevents);
                 APR_ARRAY_PUSH(m->streams_ev_out, h2_stream*) = stream;
             }
-            else if (conn_ctx->pfd_in_drain.desc.f == pfd->desc.f) {
-                /* input has been consumed */
-                ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c1,
-                              "[%s-%d] poll input event %hx",
-                              conn_ctx->id, conn_ctx->stream_id,
-                              pfd->rtnevents);
-                APR_ARRAY_PUSH(m->streams_ev_in, h2_stream*) = stream;
-            }
         }
 
         if (on_stream_input && m->streams_ev_in->nelts) {

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.h Fri Mar 18 09:52:52 2022
@@ -44,6 +44,13 @@ struct h2_iqueue;
 
 #include <apr_queue.h>
 
+typedef struct h2_c2_transit h2_c2_transit;
+
+struct h2_c2_transit {
+    apr_pool_t *pool;
+    apr_bucket_alloc_t *bucket_alloc;
+};
+
 typedef struct h2_mplx h2_mplx;
 
 struct h2_mplx {
@@ -83,14 +90,16 @@ struct h2_mplx {
     apr_array_header_t *streams_ev_in;
     apr_array_header_t *streams_ev_out;
 
-#if !H2_POLL_STREAMS
-    apr_thread_mutex_t *poll_lock; /* not the painter */
+    apr_thread_mutex_t *poll_lock; /* protect modifications of queues below */
     struct h2_iqueue *streams_input_read;  /* streams whose input has been read from */
     struct h2_iqueue *streams_output_written; /* streams whose output has been written to */
-#endif
+
     struct h2_workers *workers;     /* h2 workers process wide instance */
 
     request_rec *scratch_r;         /* pseudo request_rec for scoreboard reporting */
+
+    apr_size_t max_spare_transits;   /* max number of transit pools idling */
+    apr_array_header_t *c2_transits; /* base pools for running c2 connections */
 };
 
 apr_status_t h2_mplx_c1_child_init(apr_pool_t *pool, server_rec *s);

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Fri Mar 18 09:52:52 2022
@@ -573,11 +573,8 @@ void h2_stream_destroy(h2_stream *stream
 void h2_stream_rst(h2_stream *stream, int error_code)
 {
     stream->rst_error = error_code;
-    if (stream->input) {
-        h2_beam_abort(stream->input, stream->session->c1);
-    }
-    if (stream->output) {
-        h2_beam_abort(stream->output, stream->session->c1);
+    if (stream->c2) {
+        h2_c2_abort(stream->c2, stream->session->c1);
     }
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c1,
                   H2_STRM_MSG(stream, "reset, error=%d"), error_code);

Modified: httpd/httpd/trunk/modules/http2/h2_workers.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_workers.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_workers.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_workers.c Fri Mar 18 09:52:52 2022
@@ -40,7 +40,7 @@ struct h2_slot {
     apr_thread_t *thread;
     apr_thread_mutex_t *lock;
     apr_thread_cond_t *not_idle;
-    volatile apr_uint32_t timed_out;
+    /* atomic */ apr_uint32_t timed_out;
 };
 
 static h2_slot *pop_slot(h2_slot *volatile *phead) 
@@ -99,7 +99,7 @@ static apr_status_t activate_slot(h2_wor
     /* thread will either immediately start work or add itself
      * to the idle queue */
     apr_atomic_inc32(&workers->worker_count);
-    slot->timed_out = 0;
+    apr_atomic_set32(&slot->timed_out, 0);
     rv = ap_thread_create(&slot->thread, workers->thread_attr,
                           slot_run, slot, workers->pool);
     if (rv != APR_SUCCESS) {
@@ -125,22 +125,22 @@ static apr_status_t add_worker(h2_worker
 
 static void wake_idle_worker(h2_workers *workers) 
 {
-    h2_slot *slot = pop_slot(&workers->idle);
-    if (slot) {
-        int timed_out = 0;
-        apr_thread_mutex_lock(slot->lock);
-        timed_out = slot->timed_out;
-        if (!timed_out) {
-            apr_thread_cond_signal(slot->not_idle);
+    h2_slot *slot;;
+    for (;;) {
+        slot = pop_slot(&workers->idle);
+        if (!slot) {
+            if (workers->dynamic && apr_atomic_read32(&workers->shutdown) == 0) {
+                add_worker(workers);
+            }
+            return;
         }
-        apr_thread_mutex_unlock(slot->lock);
-        if (timed_out) {
-            slot_done(slot);
-            wake_idle_worker(workers);
+        if (!apr_atomic_read32(&slot->timed_out)) {
+            apr_thread_mutex_lock(slot->lock);
+            apr_thread_cond_signal(slot->not_idle);
+            apr_thread_mutex_unlock(slot->lock);
+            return;
         }
-    }
-    else if (workers->dynamic && !workers->shutdown) {
-        add_worker(workers);
+        slot_done(slot);
     }
 }
 
@@ -190,9 +190,10 @@ static int get_next(h2_slot *slot)
     int non_essential = slot->id >= workers->min_workers;
     apr_status_t rv;
 
-    while (!workers->aborted && !slot->timed_out) {
+    while (apr_atomic_read32(&workers->aborted) == 0
+        && apr_atomic_read32(&slot->timed_out) == 0) {
         ap_assert(slot->connection == NULL);
-        if (non_essential && workers->shutdown) {
+        if (non_essential && apr_atomic_read32(&workers->shutdown)) {
             /* Terminate non-essential worker on shutdown */
             break;
         }
@@ -208,14 +209,16 @@ static int get_next(h2_slot *slot)
         join_zombies(workers);
 
         apr_thread_mutex_lock(slot->lock);
-        if (!workers->aborted) {
+        if (apr_atomic_read32(&workers->aborted) == 0) {
+            apr_uint32_t idle_secs;
 
             push_slot(&workers->idle, slot);
-            if (non_essential && workers->max_idle_duration) {
+            if (non_essential
+                && (idle_secs = apr_atomic_read32(&workers->max_idle_secs))) {
                 rv = apr_thread_cond_timedwait(slot->not_idle, slot->lock,
-                                               workers->max_idle_duration);
+                                               apr_time_from_sec(idle_secs));
                 if (APR_TIMEUP == rv) {
-                    slot->timed_out = 1;
+                    apr_atomic_set32(&slot->timed_out, 1);
                 }
             }
             else {
@@ -237,7 +240,8 @@ static void slot_done(h2_slot *slot)
     /* If this worker is the last one exiting and the MPM child is stopping,
      * unblock workers_pool_cleanup().
      */
-    if (!apr_atomic_dec32(&workers->worker_count) && workers->aborted) {
+    if (!apr_atomic_dec32(&workers->worker_count)
+        && apr_atomic_read32(&workers->aborted)) {
         apr_thread_mutex_lock(workers->lock);
         apr_thread_cond_signal(workers->all_done);
         apr_thread_mutex_unlock(workers->lock);
@@ -254,7 +258,7 @@ static void* APR_THREAD_FUNC slot_run(ap
         do {
             ap_assert(slot->connection != NULL);
             h2_c2_process(slot->connection, thread, slot->id);
-            if (!slot->workers->aborted &&
+            if (apr_atomic_read32(&slot->workers->aborted) == 0 &&
                 apr_atomic_read32(&slot->workers->worker_count) < slot->workers->max_workers) {
                 h2_mplx_worker_c2_done(slot->connection, &slot->connection);
             }
@@ -265,7 +269,7 @@ static void* APR_THREAD_FUNC slot_run(ap
         } while (slot->connection);
     }
 
-    if (!slot->timed_out) {
+    if (apr_atomic_read32(&slot->timed_out) == 0) {
         slot_done(slot);
     }
 
@@ -294,8 +298,8 @@ static void workers_abort_idle(h2_worker
 {
     h2_slot *slot;
 
-    workers->shutdown = 1;
-    workers->aborted = 1;
+    apr_atomic_set32(&workers->shutdown, 1);
+    apr_atomic_set32(&workers->aborted, 1);
     h2_fifo_term(workers->mplxs);
 
     /* abort all idle slots */
@@ -379,12 +383,12 @@ h2_workers *h2_workers_create(server_rec
     workers->pool = pool;
     workers->min_workers = min_workers;
     workers->max_workers = max_workers;
-    workers->max_idle_duration = apr_time_from_sec((idle_secs > 0)? idle_secs : 10);
+    workers->max_idle_secs = (idle_secs > 0)? idle_secs : 10;
 
     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
                  "h2_workers: created with min=%d max=%d idle_timeout=%d sec",
                  workers->min_workers, workers->max_workers,
-                 (int)apr_time_sec(workers->max_idle_duration));
+                 (int)workers->max_idle_secs);
     /* FIXME: the fifo set we use here has limited capacity. Once the
      * set is full, connections with new requests do a wait.
      */
@@ -460,7 +464,7 @@ apr_status_t h2_workers_unregister(h2_wo
 
 void h2_workers_graceful_shutdown(h2_workers *workers)
 {
-    workers->shutdown = 1;
-    workers->max_idle_duration = apr_time_from_sec(1);
+    apr_atomic_set32(&workers->shutdown, 1);
+    apr_atomic_set32(&workers->max_idle_secs, 1);
     wake_non_essential_workers(workers);
 }

Modified: httpd/httpd/trunk/modules/http2/h2_workers.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_workers.h?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_workers.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_workers.h Fri Mar 18 09:52:52 2022
@@ -38,19 +38,17 @@ struct h2_workers {
     
     int next_worker_id;
     apr_uint32_t max_workers;
-    volatile apr_uint32_t min_workers; /* is changed during graceful shutdown */
-    volatile apr_interval_time_t max_idle_duration; /* is changed during graceful shutdown */
-    
-    volatile int aborted;
-    volatile int shutdown;
+    apr_uint32_t min_workers;
+    /* atomic */ apr_uint32_t worker_count;
+    /* atomic */ apr_uint32_t max_idle_secs;
+    /* atomic */ apr_uint32_t aborted;
+    /* atomic */ apr_uint32_t shutdown;
     int dynamic;
 
     apr_threadattr_t *thread_attr;
     int nslots;
     struct h2_slot *slots;
-    
-    volatile apr_uint32_t worker_count;
-    
+
     struct h2_slot *free;
     struct h2_slot *idle;
     struct h2_slot *zombies;

Modified: httpd/httpd/trunk/server/connection.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/connection.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/server/connection.c (original)
+++ httpd/httpd/trunk/server/connection.c Fri Mar 18 09:52:52 2022
@@ -35,6 +35,7 @@ APR_HOOK_STRUCT(
             APR_HOOK_LINK(process_connection)
             APR_HOOK_LINK(pre_connection)
             APR_HOOK_LINK(pre_close_connection)
+            APR_HOOK_LINK(create_secondary_connection)
 )
 AP_IMPLEMENT_HOOK_RUN_FIRST(conn_rec *,create_connection,
                             (apr_pool_t *p, server_rec *server, apr_socket_t *csd, long conn_id, void *sbh, apr_bucket_alloc_t *alloc),
@@ -42,6 +43,9 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(conn_rec *,c
 AP_IMPLEMENT_HOOK_RUN_FIRST(int,process_connection,(conn_rec *c),(c),DECLINED)
 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_connection,(conn_rec *c, void *csd),(c, csd),OK,DECLINED)
 AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_close_connection,(conn_rec *c),(c),OK,DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(conn_rec *,create_secondary_connection,
+                            (apr_pool_t *p, conn_rec *master, apr_bucket_alloc_t *alloc),
+                            (p, master, alloc), NULL)
 
 AP_DECLARE(conn_rec *) ap_create_connection(apr_pool_t *p,
                                             server_rec *server,
@@ -66,6 +70,13 @@ AP_DECLARE(conn_rec *) ap_create_connect
     return c;
 }
 
+AP_DECLARE(conn_rec *) ap_create_secondary_connection(apr_pool_t *p,
+                                                      conn_rec *master,
+                                                      apr_bucket_alloc_t *alloc)
+{
+    return ap_run_create_secondary_connection(p, master, alloc);
+}
+
 /*
  * More machine-dependent networking gooo... on some systems,
  * you've got to be *really* sure that all the packets are acknowledged

Modified: httpd/httpd/trunk/server/core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/core.c?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/server/core.c (original)
+++ httpd/httpd/trunk/server/core.c Fri Mar 18 09:52:52 2022
@@ -147,6 +147,8 @@ typedef struct {
     struct ap_logconf log;
 } conn_log_config;
 
+static apr_socket_t *dummy_socket;
+
 static void *create_core_dir_config(apr_pool_t *a, char *dir)
 {
     core_dir_config *conf;
@@ -5517,6 +5519,54 @@ static conn_rec *core_create_conn(apr_po
     return c;
 }
 
+static conn_rec *core_create_secondary_conn(apr_pool_t *ptrans,
+                                            conn_rec *master,
+                                            apr_bucket_alloc_t *alloc)
+{
+    apr_pool_t *pool;
+    conn_config_t *conn_config;
+    conn_rec *c = (conn_rec *) apr_pmemdup(ptrans, master, sizeof(*c));
+
+    /* Got a connection structure, so initialize what fields we can
+     * (the rest are zeroed out by pcalloc).
+     */
+    apr_pool_create(&pool, ptrans);
+    apr_pool_tag(pool, "secondary_conn");
+
+    /* we copied everything, now replace what is different */
+    c->master = master;
+    c->pool = pool;
+    c->bucket_alloc = alloc;
+    c->conn_config = ap_create_conn_config(pool);
+    c->notes = apr_table_make(pool, 5);
+    c->slaves = apr_array_make(pool, 20, sizeof(conn_slave_rec *));
+    c->requests = apr_array_make(pool, 20, sizeof(request_rec *));
+    c->input_filters = NULL;
+    c->output_filters = NULL;
+    c->filter_conn_ctx = NULL;
+
+    /* prevent mpm_event from making wrong assumptions about this connection,
+     * like e.g. using its socket for an async read check. */
+    c->clogging_input_filters = 1;
+
+    c->log = NULL;
+    c->aborted = 0;
+    c->keepalives = 0;
+
+    /* FIXME: mpms (and maybe other) parts think that there is always
+     * a socket for a connection. We cannot use the master socket for
+     * secondary connections, as this gets modified (closed?) when
+     * the secondary connection terminates.
+     * There seem to be some checks for c->master necessary in other
+     * places.
+     */
+    conn_config = apr_pcalloc(pool, sizeof(*conn_config));
+    conn_config->socket = dummy_socket;
+    ap_set_core_module_config(c->conn_config, conn_config);
+
+    return c;
+}
+
 static int core_pre_connection(conn_rec *c, void *csd)
 {
     conn_config_t *conn_config;
@@ -5670,6 +5720,11 @@ static void core_child_init(apr_pool_t *
      */
     proc.pid = getpid();
     apr_random_after_fork(&proc);
+
+    /* needed for secondary connections so people do not change the master
+     * connection socket. */
+    apr_socket_create(&dummy_socket, APR_INET, SOCK_STREAM,
+                      APR_PROTO_TCP, pchild);
 }
 
 static void core_optional_fn_retrieve(void)
@@ -5905,6 +5960,8 @@ static void register_hooks(apr_pool_t *p
      */
     ap_hook_create_connection(core_create_conn, NULL, NULL,
                               APR_HOOK_REALLY_LAST);
+    ap_hook_create_secondary_connection(core_create_secondary_conn, NULL, NULL,
+                                        APR_HOOK_REALLY_LAST);
     ap_hook_pre_connection(core_pre_connection, NULL, NULL,
                            APR_HOOK_REALLY_LAST);
 

Modified: httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py?rev=1899032&r1=1899031&r2=1899032&view=diff
==============================================================================
--- httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py (original)
+++ httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py Fri Mar 18 09:52:52 2022
@@ -25,7 +25,7 @@ class TestLoadCgi:
         assert 0 == r.results["h2load"]["status"]["5xx"]
     
     # test POST on cgi, where input is read
-    def test_h2_711_10(self, env):
+    def test_h2_711_10(self, env, repeat):
         url = env.mkurl("https", "test1", "/echo.py")
         n = 100
         m = 5
@@ -40,7 +40,7 @@ class TestLoadCgi:
         self.check_h2load_ok(env, r, n)
 
     # test POST on cgi via http/1.1 proxy, where input is read
-    def test_h2_711_11(self, env):
+    def test_h2_711_11(self, env, repeat):
         url = env.mkurl("https", "test1", "/proxy/echo.py")
         n = 100
         m = 5
@@ -55,7 +55,7 @@ class TestLoadCgi:
         self.check_h2load_ok(env, r, n)
 
     # test POST on cgi via h2proxy, where input is read
-    def test_h2_711_12(self, env):
+    def test_h2_711_12(self, env, repeat):
         url = env.mkurl("https", "test1", "/h2proxy/echo.py")
         n = 100
         m = 5



Re: svn commit: r1899032 - in /httpd/httpd/trunk: ./ build/ changes-entries/ include/ modules/http2/ server/ test/modules/http2/

Posted by Ruediger Pluem <rp...@apache.org>.

On 3/18/22 10:52 AM, icing@apache.org wrote:
> Author: icing
> Date: Fri Mar 18 09:52:52 2022
> New Revision: 1899032
> 
> URL: http://svn.apache.org/viewvc?rev=1899032&view=rev
> Log:
>   *) core: adding a new hook and method to the API:
>      create_secondary_connection and ap_create_secondary_connection()
>      to setup connections related to a "master" one, as used in
>      the HTTP/2 protocol implementation.
> 
>   *) mod_http2: using the new API calls to get rid of knowledge
>      about how the core handles conn_rec specifics.
>      Improvements in pollset stream handling to use less sets.
>      Using atomic read/writes instead of volatiles now.
>      Keeping a reserve of "transit" pools and bucket_allocs for
>      use on secondary connections to avoid repeated setup/teardowns.
> 
> 
> Added:
>     httpd/httpd/trunk/changes-entries/core_secondary_conn.txt
> Modified:
>     httpd/httpd/trunk/   (props changed)
>     httpd/httpd/trunk/build/   (props changed)
>     httpd/httpd/trunk/include/ap_mmn.h
>     httpd/httpd/trunk/include/http_connection.h
>     httpd/httpd/trunk/modules/http2/h2_c2.c
>     httpd/httpd/trunk/modules/http2/h2_c2.h
>     httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
>     httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
>     httpd/httpd/trunk/modules/http2/h2_mplx.c
>     httpd/httpd/trunk/modules/http2/h2_mplx.h
>     httpd/httpd/trunk/modules/http2/h2_stream.c
>     httpd/httpd/trunk/modules/http2/h2_workers.c
>     httpd/httpd/trunk/modules/http2/h2_workers.h
>     httpd/httpd/trunk/server/connection.c
>     httpd/httpd/trunk/server/core.c
>     httpd/httpd/trunk/test/modules/http2/test_711_load_post_cgi.py
> 


> Modified: httpd/httpd/trunk/server/core.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/core.c?rev=1899032&r1=1899031&r2=1899032&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/server/core.c (original)
> +++ httpd/httpd/trunk/server/core.c Fri Mar 18 09:52:52 2022

> @@ -5517,6 +5519,54 @@ static conn_rec *core_create_conn(apr_po
>      return c;
>  }
>  
> +static conn_rec *core_create_secondary_conn(apr_pool_t *ptrans,
> +                                            conn_rec *master,
> +                                            apr_bucket_alloc_t *alloc)
> +{
> +    apr_pool_t *pool;
> +    conn_config_t *conn_config;
> +    conn_rec *c = (conn_rec *) apr_pmemdup(ptrans, master, sizeof(*c));

Why don't we allocate the structure from the subpool below?

> +
> +    /* Got a connection structure, so initialize what fields we can
> +     * (the rest are zeroed out by pcalloc).
> +     */

I don't get the comment above. We copy the existing master conn_rec. We
don't apr_pcalloc here.


> +    apr_pool_create(&pool, ptrans);

Why do we need a subpool here? In c2_transit_create we already create
a subpool with a dedicated allocator.
Is there a usecase for a subpool without a dedicated allocator?
If yes does it make sense to allow passing a dedicated allocator to the hook
to create a subpool using it (just like in c2_transit_create) and if NULL is
supplied only create a subpool like now?


> +    apr_pool_tag(pool, "secondary_conn");
> +
> +    /* we copied everything, now replace what is different */
> +    c->master = master;
> +    c->pool = pool;
> +    c->bucket_alloc = alloc;
> +    c->conn_config = ap_create_conn_config(pool);
> +    c->notes = apr_table_make(pool, 5);
> +    c->slaves = apr_array_make(pool, 20, sizeof(conn_slave_rec *));
> +    c->requests = apr_array_make(pool, 20, sizeof(request_rec *));
> +    c->input_filters = NULL;
> +    c->output_filters = NULL;
> +    c->filter_conn_ctx = NULL;
> +
> +    /* prevent mpm_event from making wrong assumptions about this connection,
> +     * like e.g. using its socket for an async read check. */
> +    c->clogging_input_filters = 1;
> +
> +    c->log = NULL;
> +    c->aborted = 0;
> +    c->keepalives = 0;
> +
> +    /* FIXME: mpms (and maybe other) parts think that there is always
> +     * a socket for a connection. We cannot use the master socket for
> +     * secondary connections, as this gets modified (closed?) when
> +     * the secondary connection terminates.
> +     * There seem to be some checks for c->master necessary in other
> +     * places.
> +     */
> +    conn_config = apr_pcalloc(pool, sizeof(*conn_config));
> +    conn_config->socket = dummy_socket;
> +    ap_set_core_module_config(c->conn_config, conn_config);
> +
> +    return c;
> +}
> +
>  static int core_pre_connection(conn_rec *c, void *csd)
>  {
>      conn_config_t *conn_config;


Regards

RĂ¼diger