You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2018/09/12 13:56:21 UTC

svn commit: r1840688 [29/37] - in /httpd/sandbox/httpd-2.4.x-trunk-delta: ./ build/ build/rpm/ build/win32/ docs/ docs/conf/ docs/conf/extra/ docs/log-message-tags/ docs/man/ docs/man/tr/ include/ modules/ modules/aaa/ modules/apreq/ modules/arch/unix/...

Modified: httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/event/event.c
URL: http://svn.apache.org/viewvc/httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/event/event.c?rev=1840688&r1=1840687&r2=1840688&view=diff
==============================================================================
--- httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/event/event.c (original)
+++ httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/event/event.c Wed Sep 12 13:56:17 2018
@@ -57,6 +57,7 @@
 #include "apr_atomic.h"
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
+
 #include "apr_version.h"
 
 #include <stdlib.h>
@@ -95,11 +96,17 @@
 #include "http_vhost.h"
 #include "unixd.h"
 #include "apr_skiplist.h"
+#include "util_time.h"
 
 #include <signal.h>
 #include <limits.h>             /* for INT_MAX */
 
 
+#if HAVE_SERF
+#include "mod_serf.h"
+#include "serf.h"
+#endif
+
 /* Limit on the total --- clients will be locked out if more servers than
  * this are needed.  It is intended solely to keep the server from crashing
  * when things get out of hand.
@@ -331,6 +338,15 @@ static struct timeout_queue *TO_QUEUE_MA
 #define TO_QUEUE_ELEM_INIT(el) \
     APR_RING_ELEM_INIT((el), timeout_list)
 
+#if HAVE_SERF
+typedef struct {
+    apr_pollset_t *pollset;
+    apr_pool_t *pool;
+} s_baton_t;
+
+static serf_context_t *g_serf;
+#endif
+
 /* The structure used to pass unique initialization info to each thread */
 typedef struct
 {
@@ -353,6 +369,10 @@ typedef enum
 {
     PT_CSD,
     PT_ACCEPT
+#if HAVE_SERF
+    , PT_SERF
+#endif
+    , PT_USER
 } poll_type_e;
 
 typedef struct
@@ -361,6 +381,15 @@ typedef struct
     void *baton;
 } listener_poll_type;
 
+typedef struct
+{
+    ap_mpm_callback_fn_t *cbfunc;
+    void *user_baton;
+    apr_array_header_t *pfds;
+    timer_event_t *cancel_event; /* If a timeout was requested, a pointer to the timer event */
+    unsigned int signaled :1;
+} socket_callback_baton_t;
+
 /* data retained by event across load/unload of the module
  * allocated on first call to pre-config hook; located on
  * subsequent calls to pre-config hook
@@ -436,6 +465,7 @@ int raise_sigstop_flags;
 
 static apr_pool_t *pconf;       /* Pool for config stuff */
 static apr_pool_t *pchild;      /* Pool for httpd child stuff */
+static apr_pool_t *pruntime;    /* Pool for MPM threads stuff */
 
 static pid_t ap_my_pid;         /* Linux getpid() doesn't work except in main
                                    thread. Use this instead */
@@ -653,6 +683,9 @@ static int event_query(int query_code, i
     case AP_MPMQ_IS_ASYNC:
         *result = 1;
         break;
+    case AP_MPMQ_HAS_SERF:
+        *result = 1;
+        break;
     case AP_MPMQ_HARD_LIMIT_DAEMONS:
         *result = server_limit;
         break;
@@ -686,6 +719,12 @@ static int event_query(int query_code, i
     case AP_MPMQ_GENERATION:
         *result = retained->mpm->my_generation;
         break;
+    case AP_MPMQ_CAN_SUSPEND:
+        *result = 1;
+        break;
+    case AP_MPMQ_CAN_POLL:
+        *result = 1;
+        break;
     default:
         *rv = APR_ENOTIMPL;
         break;
@@ -709,10 +748,10 @@ static void event_note_child_killed(int
 
 static void event_note_child_started(int slot, pid_t pid)
 {
+    ap_generation_t gen = retained->mpm->my_generation;
     ap_scoreboard_image->parent[slot].pid = pid;
-    ap_run_child_status(ap_server_conf,
-                        ap_scoreboard_image->parent[slot].pid,
-                        retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
+    ap_scoreboard_image->parent[slot].generation = gen;
+    ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
 }
 
 static const char *event_get_name(void)
@@ -954,7 +993,7 @@ static void process_socket(apr_thread_t
 {
     conn_rec *c;
     long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
-    int clogging = 0;
+    int clogging = 0, from_wc_q = 0;
     apr_status_t rv;
     int rc = OK;
 
@@ -1056,6 +1095,9 @@ read_request:
                 rc = OK;
             }
         }
+        else if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) {
+            from_wc_q = 1;
+    }
     }
     /*
      * The process_connection hooks above should set the connection state
@@ -1099,19 +1141,18 @@ read_request:
     }
 
     if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) {
-        ap_filter_t *output_filter = c->output_filters;
-        apr_status_t rv;
+        int pending = DECLINED;
+
         ap_update_child_status(cs->sbh, SERVER_BUSY_WRITE, NULL);
-        while (output_filter->next != NULL) {
-            output_filter = output_filter->next;
+
+        if (from_wc_q) {
+            from_wc_q = 0; /* one shot */
+            pending = ap_run_output_pending(c);
         }
-        rv = output_filter->frec->filter_func.out_func(output_filter, NULL);
-        if (rv != APR_SUCCESS) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00470)
-                          "network write failure in core output filter");
-            cs->pub.state = CONN_STATE_LINGER;
+        else if (ap_filter_should_yield(c->output_filters)) {
+            pending = OK;
         }
-        else if (c->data_in_output_filters) {
+        if (pending == OK) {
             /* Still in WRITE_COMPLETION_STATE:
              * Set a write timeout for this connection, and let the
              * event thread poll for writeability.
@@ -1150,11 +1191,13 @@ read_request:
             }
             return;
         }
-        else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted ||
-                 listener_may_exit) {
+        if (pending != DECLINED
+                || c->aborted
+                || c->keepalive != AP_CONN_KEEPALIVE
+                || listener_may_exit) {
             cs->pub.state = CONN_STATE_LINGER;
         }
-        else if (c->data_in_input_filters) {
+        else if (ap_run_input_pending(c) == OK) {
             cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
             goto read_request;
         }
@@ -1199,6 +1242,7 @@ read_request:
     }
 
     if (cs->pub.state == CONN_STATE_SUSPENDED) {
+        cs->c->suspended_baton = cs;
         apr_atomic_inc32(&suspended_count);
         notify_suspend(cs);
         return;
@@ -1213,6 +1257,35 @@ read_request:
     }
 }
 
+/* Put a SUSPENDED connection back into a queue. */
+static apr_status_t event_resume_suspended (conn_rec *c)
+{
+    event_conn_state_t* cs = (event_conn_state_t*) c->suspended_baton;
+    if (cs == NULL) {
+        ap_log_cerror (APLOG_MARK, LOG_WARNING, 0, c, APLOGNO(02615)
+                "event_resume_suspended: suspended_baton is NULL");
+        return APR_EGENERAL;
+    } else if (!cs->suspended) {
+        ap_log_cerror (APLOG_MARK, LOG_WARNING, 0, c, APLOGNO(02616)
+                "event_resume_suspended: Thread isn't suspended");
+        return APR_EGENERAL;
+    }
+    apr_atomic_dec32(&suspended_count);
+    c->suspended_baton = NULL;
+
+    cs->queue_timestamp = apr_time_now();
+    cs->pfd.reqevents = (
+            cs->pub.sense == CONN_SENSE_WANT_READ ? APR_POLLIN :
+                    APR_POLLOUT) | APR_POLLHUP | APR_POLLERR;
+    cs->pub.sense = CONN_SENSE_DEFAULT;
+    apr_thread_mutex_lock(timeout_mutex);
+    TO_QUEUE_APPEND(cs->sc->wc_q, cs);
+    apr_pollset_add(event_pollset, &cs->pfd);
+    apr_thread_mutex_unlock(timeout_mutex);
+
+    return OK;
+}
+
 /* conns_this_child has gone to zero or below.  See if the admin coded
    "MaxConnectionsPerChild 0", and keep going in that case.  Doing it this way
    simplifies the hot path in worker_thread */
@@ -1270,35 +1343,49 @@ static void dummy_signal_handler(int sig
 }
 
 
-static apr_status_t init_pollset(apr_pool_t *p)
+#if HAVE_SERF
+static apr_status_t s_socket_add(void *user_baton,
+                                 apr_pollfd_t *pfd,
+                                 void *serf_baton)
 {
-    ap_listen_rec *lr;
-    listener_poll_type *pt;
-    int i = 0;
+    s_baton_t *s = (s_baton_t*)user_baton;
+    /* XXXXX: recycle listener_poll_types */
+    listener_poll_type *pt = ap_malloc(sizeof(*pt));
+    pt->type = PT_SERF;
+    pt->baton = serf_baton;
+    pfd->client_data = pt;
+    return apr_pollset_add(s->pollset, pfd);
+}
 
-    listener_pollfd = apr_palloc(p, sizeof(apr_pollfd_t) * num_listensocks);
-    for (lr = my_bucket->listeners; lr != NULL; lr = lr->next, i++) {
-        apr_pollfd_t *pfd;
-        AP_DEBUG_ASSERT(i < num_listensocks);
-        pfd = &listener_pollfd[i];
-        pt = apr_pcalloc(p, sizeof(*pt));
-        pfd->desc_type = APR_POLL_SOCKET;
-        pfd->desc.s = lr->sd;
-        pfd->reqevents = APR_POLLIN;
+static apr_status_t s_socket_remove(void *user_baton,
+                                    apr_pollfd_t *pfd,
+                                    void *serf_baton)
+{
+    s_baton_t *s = (s_baton_t*)user_baton;
+    listener_poll_type *pt = pfd->client_data;
+    free(pt);
+    return apr_pollset_remove(s->pollset, pfd);
+}
+#endif
 
-        pt->type = PT_ACCEPT;
-        pt->baton = lr;
+#if HAVE_SERF
+static void init_serf(apr_pool_t *p)
+{
+    s_baton_t *baton = NULL;
 
-        pfd->client_data = pt;
+    baton = apr_pcalloc(p, sizeof(*baton));
+    baton->pollset = event_pollset;
+    /* TODO: subpools, threads, reuse, etc.  -- currently use malloc() inside :( */
+    baton->pool = p;
 
-        apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
-        apr_pollset_add(event_pollset, pfd);
+    g_serf = serf_context_create_ex(baton,
+                                    s_socket_add,
+                                    s_socket_remove, p);
 
-        lr->accept_func = ap_unixd_accept;
+    ap_register_provider(p, "mpm_serf",
+                         "instance", "0", g_serf);
     }
-
-    return APR_SUCCESS;
-}
+#endif
 
 static apr_status_t push_timer2worker(timer_event_t* te)
 {
@@ -1411,12 +1498,17 @@ static int timer_comp(void *a, void *b)
 
 static apr_thread_mutex_t *g_timer_skiplist_mtx;
 
-static apr_status_t event_register_timed_callback(apr_time_t t,
+static timer_event_t * event_get_timer_event(apr_time_t t,
                                                   ap_mpm_callback_fn_t *cbfn,
-                                                  void *baton)
+                                             void *baton,
+                                             int insert, 
+                                             apr_array_header_t *remove)
 {
     timer_event_t *te;
+    apr_time_t now = (t < 0) ? 0 : apr_time_now();
+
     /* oh yeah, and make locking smarter/fine grained. */
+
     apr_thread_mutex_lock(g_timer_skiplist_mtx);
 
     if (!APR_RING_EMPTY(&timer_free_ring, timer_event_t, link)) {
@@ -1430,10 +1522,11 @@ static apr_status_t event_register_timed
 
     te->cbfunc = cbfn;
     te->baton = baton;
-    /* XXXXX: optimize */
-    te->when = t + apr_time_now();
+    te->canceled = 0;
+    te->when = now + t;
+    te->remove = remove;
 
-    { 
+    if (insert) { 
         apr_time_t next_expiry;
 
         /* Okay, add sorted by when.. */
@@ -1451,12 +1544,102 @@ static apr_status_t event_register_timed
             }
         }
     }
-
     apr_thread_mutex_unlock(g_timer_skiplist_mtx);
 
+    return te;
+}
+
+static apr_status_t event_register_timed_callback_ex(apr_time_t t,
+                                                  ap_mpm_callback_fn_t *cbfn,
+                                                  void *baton, 
+                                                  apr_array_header_t *remove)
+{
+    event_get_timer_event(t, cbfn, baton, 1, remove);
+    return APR_SUCCESS;
+}
+
+static apr_status_t event_register_timed_callback(apr_time_t t,
+                                                  ap_mpm_callback_fn_t *cbfn,
+                                                  void *baton)
+{
+    event_register_timed_callback_ex(t, cbfn, baton, NULL);
     return APR_SUCCESS;
 }
 
+static apr_status_t event_cleanup_poll_callback(void *data)
+{
+    apr_status_t final_rc = APR_SUCCESS;
+    apr_array_header_t *pfds = data;
+    int i;
+
+    for (i = 0; i < pfds->nelts; i++) {
+        apr_pollfd_t *pfd = (apr_pollfd_t *)pfds->elts + i;
+        if (pfd->client_data) {
+            apr_status_t rc;
+            rc = apr_pollset_remove(event_pollset, pfd);
+            if (rc != APR_SUCCESS && !APR_STATUS_IS_NOTFOUND(rc)) {
+                final_rc = rc;
+            }
+        }
+    }
+
+    return final_rc;
+}
+
+static apr_status_t event_register_poll_callback_ex(apr_array_header_t *pfds,
+                                                  ap_mpm_callback_fn_t *cbfn,
+                                                  ap_mpm_callback_fn_t *tofn,
+                                                  void *baton,
+                                                  apr_time_t timeout)
+{
+    socket_callback_baton_t *scb = apr_pcalloc(pfds->pool, sizeof(*scb));
+    listener_poll_type *pt = apr_palloc(pfds->pool, sizeof(*pt));
+    apr_status_t rc, final_rc= APR_SUCCESS;
+    int i;
+
+    pt->type = PT_USER;
+    pt->baton = scb;
+
+    scb->cbfunc = cbfn;
+    scb->user_baton = baton;
+    scb->pfds = pfds;
+
+    apr_pool_pre_cleanup_register(pfds->pool, pfds, event_cleanup_poll_callback);
+
+    for (i = 0; i < pfds->nelts; i++) {
+        apr_pollfd_t *pfd = (apr_pollfd_t *)pfds->elts + i;
+        pfd->reqevents = (pfd->reqevents) | APR_POLLERR | APR_POLLHUP;
+        pfd->client_data = pt;
+    }
+
+    if (timeout > 0) { 
+        /* XXX:  This cancel timer event count fire before the pollset is updated */
+        scb->cancel_event = event_get_timer_event(timeout, tofn, baton, 1, pfds);
+    }
+    for (i = 0; i < pfds->nelts; i++) {
+        apr_pollfd_t *pfd = (apr_pollfd_t *)pfds->elts + i;
+        rc = apr_pollset_add(event_pollset, pfd);
+        if (rc != APR_SUCCESS) {
+            final_rc = rc;
+        }
+    }
+    return final_rc;
+}
+
+static apr_status_t event_register_poll_callback(apr_array_header_t *pfds,
+                                                 ap_mpm_callback_fn_t *cbfn,
+                                                 void *baton)
+{
+    return event_register_poll_callback_ex(pfds,
+                                           cbfn,
+                                           NULL, /* no timeout function */
+                                           baton,
+                                           0     /* no timeout */);
+}
+static apr_status_t event_unregister_poll_callback(apr_array_header_t *pfds)
+{
+    return apr_pool_cleanup_run(pfds->pool, pfds, event_cleanup_poll_callback);
+}
 
 /*
  * Close socket and clean up if remote closed its end while we were in
@@ -1611,7 +1794,6 @@ static void * APR_THREAD_FUNC listener_t
     proc_info *ti = dummy;
     int process_slot = ti->pslot;
     struct process_score *ps = ap_get_scoreboard_process(process_slot);
-    apr_pool_t *tpool = apr_thread_pool_get(thd);
     int closed = 0;
     int have_idle_worker = 0;
     apr_time_t last_log;
@@ -1619,15 +1801,9 @@ static void * APR_THREAD_FUNC listener_t
     last_log = apr_time_now();
     free(ti);
 
-    rc = init_pollset(tpool);
-    if (rc != APR_SUCCESS) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
-                     "failed to initialize pollset, "
-                     "shutdown process now");
-        resource_shortage = 1;
-        signal_threads(ST_UNGRACEFUL);
-        return NULL;
-    }
+#if HAVE_SERF
+    init_serf(apr_thread_pool_get(thd));
+#endif
 
     /* Unblock the signal used to wake this thread up, and set a handler for
      * it.
@@ -1653,8 +1829,8 @@ static void * APR_THREAD_FUNC listener_t
                 break;
         }
 
-        now = apr_time_now();
         if (APLOGtrace6(ap_server_conf)) {
+        now = apr_time_now();
             /* trace log status every second */
             if (now - last_log > apr_time_from_sec(1)) {
                 last_log = now;
@@ -1678,6 +1854,15 @@ static void * APR_THREAD_FUNC listener_t
             }
         }
 
+#if HAVE_SERF
+        rc = serf_context_prerun(g_serf);
+        if (rc != APR_SUCCESS) {
+            /* TOOD: what should do here? ugh. */
+        }
+#endif
+
+        now = apr_time_now();
+
         /* Start with an infinite poll() timeout and update it according to
          * the next expiring timer or queue entry. If there are none, either
          * the listener is wakeable and it can poll() indefinitely until a wake
@@ -1699,8 +1884,22 @@ static void * APR_THREAD_FUNC listener_t
                     break;
                 }
                 apr_skiplist_pop(timer_skiplist, NULL);
+                if (!te->canceled) { 
+                    if (te->remove) {
+                        int i;
+                        for (i = 0; i < te->remove->nelts; i++) {
+                            apr_pollfd_t *pfd;
+                            pfd = (apr_pollfd_t *)te->remove->elts + i;
+                            apr_pollset_remove(event_pollset, pfd);
+                        }
+                    }
                 push_timer2worker(te);
             }
+                else {
+                    APR_RING_INSERT_TAIL(&timer_free_ring, te,
+                                         timer_event_t, link);
+                }
+            }
             if (!te) {
                 timers_next_expiry = 0;
             }
@@ -1740,6 +1939,7 @@ static void * APR_THREAD_FUNC listener_t
             }
             else if (!APR_STATUS_IS_TIMEUP(rc)) {
                 ap_log_error(APLOG_MARK, APLOG_CRIT, rc, ap_server_conf,
+                             APLOGNO(03267)
                              "apr_pollset_poll failed.  Attempting to "
                              "shutdown process gracefully");
                 signal_threads(ST_GRACEFUL);
@@ -1836,12 +2036,14 @@ static void * APR_THREAD_FUNC listener_t
                 if (workers_were_busy) {
                     disable_listensocks();
                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+                                 APLOGNO(03268)
                                  "All workers busy, not accepting new conns "
                                  "in this process");
                 }
                 else if (connections_above_limit()) {
                     disable_listensocks();
                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+                                 APLOGNO(03269)
                                  "Too many open connections (%u), "
                                  "not accepting new conns in this process",
                                  apr_atomic_read32(&connection_count));
@@ -1897,6 +2099,10 @@ static void * APR_THREAD_FUNC listener_t
                         resource_shortage = 1;
                         signal_threads(ST_GRACEFUL);
                     }
+                    else if (ap_accept_error_is_nonfatal(rc)) { 
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf, 
+                                     "accept() on client socket failed");
+                    }
 
                     if (csd != NULL) {
                         conns_this_child--;
@@ -1909,6 +2115,40 @@ static void * APR_THREAD_FUNC listener_t
                     }
                 }
             }               /* if:else on pt->type */
+#if HAVE_SERF
+            else if (pt->type == PT_SERF) {
+                /* send socket to serf. */
+                /* XXXX: this doesn't require get_worker() */
+                serf_event_trigger(g_serf, pt->baton, out_pfd);
+            }
+
+#endif
+            else if (pt->type == PT_USER) {
+                /* masquerade as a timer event that is firing */
+                int i = 0;
+                socket_callback_baton_t *baton = (socket_callback_baton_t *) pt->baton;
+                if (baton->cancel_event) {
+                    baton->cancel_event->canceled = 1;
+                }
+
+                /* We only signal once per N sockets with this baton */
+                if (!(baton->signaled)) { 
+                    baton->signaled = 1;
+                    te = event_get_timer_event(-1 /* fake timer */, 
+                                               baton->cbfunc, 
+                                               baton->user_baton, 
+                                               0, /* don't insert it */
+                                               NULL /* no associated socket callback */);
+                    /* remove all sockets in my set */
+                    for (i = 0; i < baton->pfds->nelts; i++) {
+                        apr_pollfd_t *pfd = (apr_pollfd_t *)baton->pfds->elts + i;
+                        apr_pollset_remove(event_pollset, pfd);
+                        pfd->client_data = NULL;
+                    }
+
+                    push_timer2worker(te);
+                }
+            }
         } /* for processing poll */
 
         /* XXX possible optimization: stash the current time for use as
@@ -2056,6 +2296,7 @@ static void *APR_THREAD_FUNC worker_thre
             rv = ap_queue_info_set_idle(worker_queue_info, NULL);
             if (rv != APR_SUCCESS) {
                 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+                             APLOGNO(03270)
                              "ap_queue_info_set_idle failed. Attempting to "
                              "shutdown process gracefully.");
                 signal_threads(ST_GRACEFUL);
@@ -2108,7 +2349,6 @@ static void *APR_THREAD_FUNC worker_thre
         }
         if (te != NULL) {
             te->cbfunc(te->baton);
-
             {
                 apr_thread_mutex_lock(g_timer_skiplist_mtx);
                 APR_RING_INSERT_TAIL(&timer_free_ring, te, timer_event_t, link);
@@ -2168,8 +2408,6 @@ static int check_signal(int signum)
     return 0;
 }
 
-
-
 static void create_listener_thread(thread_starter * ts)
 {
     int my_child_num = ts->child_num_arg;
@@ -2181,7 +2419,7 @@ static void create_listener_thread(threa
     my_info->pslot = my_child_num;
     my_info->tslot = -1;      /* listener thread doesn't have a thread slot */
     rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
-                           my_info, pchild);
+                           my_info, pruntime);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00474)
                      "apr_thread_create: unable to create listener thread");
@@ -2191,25 +2429,12 @@ static void create_listener_thread(threa
     apr_os_thread_get(&listener_os_thread, ts->listener);
 }
 
-/* XXX under some circumstances not understood, children can get stuck
- *     in start_threads forever trying to take over slots which will
- *     never be cleaned up; for now there is an APLOG_DEBUG message issued
- *     every so often when this condition occurs
- */
-static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+static void setup_threads_runtime(void)
 {
-    thread_starter *ts = dummy;
-    apr_thread_t **threads = ts->threads;
-    apr_threadattr_t *thread_attr = ts->threadattr;
-    int my_child_num = ts->child_num_arg;
-    proc_info *my_info;
     apr_status_t rv;
-    int i;
-    int threads_created = 0;
-    int listener_started = 0;
-    int loops;
-    int prev_threads_created;
-    int max_recycled_pools = -1;
+    ap_listen_rec *lr;
+    apr_pool_t *pskip = NULL;
+    int max_recycled_pools = -1, i;
     const int good_methods[] = { APR_POLLSET_KQUEUE,
                                  APR_POLLSET_PORT,
                                  APR_POLLSET_EPOLL };
@@ -2218,10 +2443,39 @@ static void *APR_THREAD_FUNC start_threa
     const apr_uint32_t pollset_size = (apr_uint32_t)num_listensocks +
                                       (apr_uint32_t)threads_per_child *
                                       (async_factor > 2 ? async_factor : 2);
+    int pollset_flags;
+
+    /* Event's skiplist operations will happen concurrently with other modules'
+     * runtime so they need their own pool for allocations, and its lifetime
+     * should be at least the one of the connections (ptrans). Thus pskip is
+     * created as a subpool of pconf like/before ptrans (before so that it's
+     * destroyed after). In forked mode pconf is never destroyed so we are good
+     * anyway, but in ONE_PROCESS mode this ensures that the skiplist works
+     * from connection/ptrans cleanups (even after pchild is destroyed).
+     */
+    apr_pool_create(&pskip, pconf);
+    apr_pool_tag(pskip, "mpm_skiplist");
+    apr_thread_mutex_create(&g_timer_skiplist_mtx, APR_THREAD_MUTEX_DEFAULT, pskip);
+    APR_RING_INIT(&timer_free_ring, timer_event_t, link);
+    apr_skiplist_init(&timer_skiplist, pskip);
+    apr_skiplist_set_compare(timer_skiplist, timer_comp, timer_comp);
+
+    /* All threads (listener, workers) and synchronization objects (queues,
+     * pollset, mutexes...) created here should have at least the lifetime of
+     * the connections they handle (i.e. ptrans). We can't use this thread's
+     * self pool because all these objects survive it, nor use pchild or pconf
+     * directly because this starter thread races with other modules' runtime,
+     * nor finally pchild (or subpool thereof) because it is killed explicitely
+     * before pconf (thus connections/ptrans can live longer, which matters in
+     * ONE_PROCESS mode). So this leaves us with a subpool of pconf, created
+     * before any ptrans hence destroyed after.
+     */
+    apr_pool_create(&pruntime, pconf);
+    apr_pool_tag(pruntime, "mpm_runtime");
 
     /* We must create the fd queues before we start up the listener
      * and worker threads. */
-    rv = ap_queue_create(&worker_queue, threads_per_child, pchild);
+    rv = ap_queue_create(&worker_queue, threads_per_child, pruntime);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03100)
                      "ap_queue_create() failed");
@@ -2235,7 +2489,7 @@ static void *APR_THREAD_FUNC start_threa
          */
         max_recycled_pools = threads_per_child * 3 / 4 ;
     }
-    rv = ap_queue_info_create(&worker_queue_info, pchild,
+    rv = ap_queue_info_create(&worker_queue_info, pruntime,
                               threads_per_child, max_recycled_pools);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03101)
@@ -2247,7 +2501,7 @@ static void *APR_THREAD_FUNC start_threa
      * thread starts.
      */
     rv = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT,
-                                 pchild);
+                                 pruntime);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03102)
                      "creation of the timeout mutex failed.");
@@ -2255,25 +2509,30 @@ static void *APR_THREAD_FUNC start_threa
     }
 
     /* Create the main pollset */
+    pollset_flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY |
+                    APR_POLLSET_NODEFAULT | APR_POLLSET_WAKEABLE;
     for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) {
-        apr_uint32_t flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY |
-                             APR_POLLSET_NODEFAULT | APR_POLLSET_WAKEABLE;
-        rv = apr_pollset_create_ex(&event_pollset, pollset_size, pchild, flags,
-                                   good_methods[i]);
+        rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime,
+                                   pollset_flags, good_methods[i]);
         if (rv == APR_SUCCESS) {
             listener_is_wakeable = 1;
             break;
         }
-        flags &= ~APR_POLLSET_WAKEABLE;
-        rv = apr_pollset_create_ex(&event_pollset, pollset_size, pchild, flags,
-                                   good_methods[i]);
+    }
+    if (rv != APR_SUCCESS) {
+        pollset_flags &= ~APR_POLLSET_WAKEABLE;
+        for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) {
+            rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime,
+                                       pollset_flags, good_methods[i]);
         if (rv == APR_SUCCESS) {
             break;
         }
     }
+    }
     if (rv != APR_SUCCESS) {
-        rv = apr_pollset_create(&event_pollset, pollset_size, pchild,
-                                APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY);
+        pollset_flags &= ~APR_POLLSET_NODEFAULT;
+        rv = apr_pollset_create(&event_pollset, pollset_size, pruntime,
+                                pollset_flags);
     }
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03103)
@@ -2281,12 +2540,57 @@ static void *APR_THREAD_FUNC start_threa
         clean_child_exit(APEXIT_CHILDFATAL);
     }
 
+    /* Add listeners to the main pollset */
+    listener_pollfd = apr_pcalloc(pruntime, num_listensocks *
+                                            sizeof(apr_pollfd_t));
+    for (i = 0, lr = my_bucket->listeners; lr; lr = lr->next, i++) {
+        apr_pollfd_t *pfd;
+        listener_poll_type *pt;
+
+        AP_DEBUG_ASSERT(i < num_listensocks);
+        pfd = &listener_pollfd[i];
+
+        pfd->reqevents = APR_POLLIN;
+        pfd->desc_type = APR_POLL_SOCKET;
+        pfd->desc.s = lr->sd;
+
+        pt = apr_pcalloc(pruntime, sizeof(*pt));
+        pfd->client_data = pt;
+        pt->type = PT_ACCEPT;
+        pt->baton = lr;
+
+        apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
+        apr_pollset_add(event_pollset, pfd);
+
+        lr->accept_func = ap_unixd_accept;
+    }
+
+    worker_sockets = apr_pcalloc(pruntime, threads_per_child *
+                                           sizeof(apr_socket_t *));
+}
+
+/* XXX under some circumstances not understood, children can get stuck
+ *     in start_threads forever trying to take over slots which will
+ *     never be cleaned up; for now there is an APLOG_DEBUG message issued
+ *     every so often when this condition occurs
+ */
+static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+{
+    thread_starter *ts = dummy;
+    apr_thread_t **threads = ts->threads;
+    apr_threadattr_t *thread_attr = ts->threadattr;
+    int my_child_num = ts->child_num_arg;
+    proc_info *my_info;
+    apr_status_t rv;
+    int threads_created = 0;
+    int listener_started = 0;
+    int prev_threads_created;
+    int loops, i;
+
     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02471)
                  "start_threads: Using %s (%swakeable)",
                  apr_pollset_method_name(event_pollset),
                  listener_is_wakeable ? "" : "not ");
-    worker_sockets = apr_pcalloc(pchild, threads_per_child
-                                 * sizeof(apr_socket_t *));
 
     loops = prev_threads_created = 0;
     while (1) {
@@ -2310,7 +2614,7 @@ static void *APR_THREAD_FUNC start_threa
              * done because it lets us deal with tid better.
              */
             rv = apr_thread_create(&threads[i], thread_attr,
-                                   worker_thread, my_info, pchild);
+                                   worker_thread, my_info, pruntime);
             if (rv != APR_SUCCESS) {
                 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
                              APLOGNO(03104)
@@ -2337,6 +2641,7 @@ static void *APR_THREAD_FUNC start_threa
         if (loops % 120 == 0) { /* every couple of minutes */
             if (prev_threads_created == threads_created) {
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+                             APLOGNO(03271)
                              "child %" APR_PID_T_FMT " isn't taking over "
                              "slots very quickly (%d of %d)",
                              ap_my_pid, threads_created,
@@ -2431,7 +2736,6 @@ static void child_main(int child_num_arg
     apr_threadattr_t *thread_attr;
     apr_thread_t *start_thread_id;
     int i;
-    apr_pool_t *pskip;
 
     /* for benefit of any hooks that run as this child initializes */
     retained->mpm->mpm_state = AP_MPMQ_STARTING;
@@ -2439,7 +2743,12 @@ static void child_main(int child_num_arg
     ap_my_pid = getpid();
     ap_child_slot = child_num_arg;
     ap_fatal_signal_child_setup(ap_server_conf);
+
+    /* Get a sub context for global allocations in this child, so that
+     * we can have cleanups occur when the child exits.
+     */
     apr_pool_create(&pchild, pconf);
+    apr_pool_tag(pchild, "pchild");
 
     /* close unused listeners and pods */
     for (i = 0; i < retained->mpm->num_buckets; i++) {
@@ -2457,12 +2766,6 @@ static void child_main(int child_num_arg
         clean_child_exit(APEXIT_CHILDFATAL);
     }
 
-    apr_thread_mutex_create(&g_timer_skiplist_mtx, APR_THREAD_MUTEX_DEFAULT, pchild);
-    APR_RING_INIT(&timer_free_ring, timer_event_t, link);
-    apr_pool_create(&pskip, pchild);
-    apr_skiplist_init(&timer_skiplist, pskip);
-    apr_skiplist_set_compare(timer_skiplist, timer_comp, timer_comp);
-
     /* Just use the standard apr_setup_signal_thread to block all signals
      * from being received.  The child processes no longer use signals for
      * any communication with the parent process. Let's also do this before
@@ -2486,7 +2789,10 @@ static void child_main(int child_num_arg
         conns_this_child = APR_INT32_MAX;
     }
 
-    /* Setup worker threads */
+    /* Setup threads */
+
+    /* Globals used by signal_threads() so to be initialized before */
+    setup_threads_runtime();
 
     /* clear the storage; we may not create all our threads immediately,
      * and we want a 0 entry to indicate a thread which was not created
@@ -3257,7 +3563,7 @@ static int event_open_logs(apr_pool_t *
 
     if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
         ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
-                     (startup ? NULL : s),
+                     (startup ? NULL : s), APLOGNO(03272)
                      "no listening sockets available, shutting down");
         return !OK;
     }
@@ -3272,7 +3578,7 @@ static int event_open_logs(apr_pool_t *
     if ((rv = ap_duplicate_listeners(pconf, ap_server_conf,
                                      &listen_buckets, &num_buckets))) {
         ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
-                     (startup ? NULL : s),
+                     (startup ? NULL : s), APLOGNO(03273)
                      "could not duplicate listeners");
         return !OK;
     }
@@ -3282,7 +3588,7 @@ static int event_open_logs(apr_pool_t *
         if (!one_process && /* no POD in one_process mode */
                 (rv = ap_mpm_podx_open(pconf, &all_buckets[i].pod))) {
             ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
-                         (startup ? NULL : s),
+                         (startup ? NULL : s), APLOGNO(03274)
                          "could not open pipe-of-death");
             return !OK;
         }
@@ -3707,6 +4013,7 @@ static void event_hooks(apr_pool_t * p)
      */
     static const char *const aszSucc[] = { "core.c", NULL };
     one_process = 0;
+    ap_force_set_tz(p);
 
     ap_hook_open_logs(event_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
     /* we need to set the MPM state before other pre-config hooks use MPM query
@@ -3719,9 +4026,16 @@ static void event_hooks(apr_pool_t * p)
     ap_hook_mpm_query(event_query, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_register_timed_callback(event_register_timed_callback, NULL, NULL,
                                         APR_HOOK_MIDDLE);
+    ap_hook_mpm_register_poll_callback(event_register_poll_callback, NULL, NULL,
+                                        APR_HOOK_MIDDLE);
+    ap_hook_mpm_register_poll_callback_timeout(event_register_poll_callback_ex, NULL, NULL,
+                                        APR_HOOK_MIDDLE);
+    ap_hook_mpm_unregister_poll_callback(event_unregister_poll_callback, NULL, NULL,
+                                        APR_HOOK_MIDDLE);
     ap_hook_pre_read_request(event_pre_read_request, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_read_request(event_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_mpm_get_name(event_get_name, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_mpm_resume_suspended(event_resume_suspended, NULL, NULL, APR_HOOK_MIDDLE);
 
     ap_hook_pre_connection(event_pre_connection, NULL, NULL, APR_HOOK_REALLY_FIRST);
     ap_hook_protocol_switch(event_protocol_switch, NULL, NULL, APR_HOOK_REALLY_FIRST);

Added: httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/MOTORZ.README
URL: http://svn.apache.org/viewvc/httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/MOTORZ.README?rev=1840688&view=auto
==============================================================================
--- httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/MOTORZ.README (added)
+++ httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/MOTORZ.README Wed Sep 12 13:56:17 2018
@@ -0,0 +1,10 @@
+The MotorZ MPM aims to create a single MPM, that runs on all modern
+Unix and Win32 platforms, by levering APR as much as possible.
+
+The MoteZ MPM uses an APR Poll CB event system, with  timers being
+built in.  When an event, either an I/O or Timer is ready to run, it
+is pushed to any available thread in the current process. A simple
+APR Thread pool is used.
+
+MotorZ uses Prefork as the framework and Simple for the actual event
+structure.

Added: httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/Makefile.in
URL: http://svn.apache.org/viewvc/httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/Makefile.in?rev=1840688&view=auto
==============================================================================
--- httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/Makefile.in (added)
+++ httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/Makefile.in Wed Sep 12 13:56:17 2018
@@ -0,0 +1 @@
+include $(top_srcdir)/build/special.mk

Added: httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config.m4
URL: http://svn.apache.org/viewvc/httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config.m4?rev=1840688&view=auto
==============================================================================
--- httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config.m4 (added)
+++ httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config.m4 Wed Sep 12 13:56:17 2018
@@ -0,0 +1,11 @@
+AC_MSG_CHECKING(if motorz MPM supports this platform)
+if test $forking_mpms_supported != yes; then
+    AC_MSG_RESULT(no - This is not a forking platform)
+elif test $ac_cv_define_APR_HAS_THREADS != yes; then
+    AC_MSG_RESULT(no - APR does not support threads)
+elif test $have_threaded_sig_graceful != yes; then
+    AC_MSG_RESULT(no - SIG_GRACEFUL cannot be used with a threaded MPM)
+else
+    AC_MSG_RESULT(yes)
+    APACHE_MPM_SUPPORTED(motorz, yes, yes)
+fi

Added: httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config3.m4
URL: http://svn.apache.org/viewvc/httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config3.m4?rev=1840688&view=auto
==============================================================================
--- httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config3.m4 (added)
+++ httpd/sandbox/httpd-2.4.x-trunk-delta/server/mpm/motorz/config3.m4 Wed Sep 12 13:56:17 2018
@@ -0,0 +1,11 @@
+APACHE_MPMPATH_INIT(motorz)
+
+dnl ## XXX - Need a more thorough check of the proper flags to use
+
+APACHE_SUBST(MOD_MPM_MOTORZ_LDADD)
+
+APACHE_MPM_MODULE(motorz, $enable_mpm_motorz, motorz.lo,[
+    AC_CHECK_FUNCS(pthread_kill)
+], , [\$(MOD_MPM_MOTORZ_LDADD)])
+
+APACHE_MPMPATH_FINISH