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