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 */
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)
}
-static apr_status_t init_pollset(apr_pool_t *p)
-{
- ap_listen_rec *lr;
- listener_poll_type *pt;
- int i = 0;
-
- 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;
-
- pt->type = PT_ACCEPT;
- pt->baton = lr;
-
- pfd->client_data = pt;
-
- apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
- apr_pollset_add(event_pollset, pfd);
-
- lr->accept_func = ap_unixd_accept;
- }
-
- return APR_SUCCESS;
-}
-
static apr_status_t push_timer2worker(timer_event_t* te)
{
return ap_queue_push_timer(worker_queue, te);
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;
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;
- }
-
/* Unblock the signal used to wake this thread up, and set a handler for
* it.
*/
return 0;
}
-
-
static void create_listener_thread(thread_starter * ts)
{
int my_child_num = ts->child_num_arg;
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");
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 };
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");
*/
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)
* 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.");
}
/* 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) {
- break;
+ }
+ 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)
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) {
* 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)
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;
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++) {
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
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
static int resource_shortage = 0;
static fd_queue_t *worker_queue;
static fd_queue_info_t *worker_queue_info;
+static apr_pollset_t *worker_pollset;
+
/* data retained by worker across load/unload of the module
* allocated on first call to pre-config hook; located on
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 */
static void worker_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 void worker_note_child_lost_slot(int slot, pid_t newpid)
{
proc_info * ti = dummy;
int process_slot = ti->pid;
- apr_pool_t *tpool = apr_thread_pool_get(thd);
void *csd = NULL;
apr_pool_t *ptrans = NULL; /* Pool for per-transaction stuff */
- apr_pollset_t *pollset;
apr_status_t rv;
- ap_listen_rec *lr;
+ ap_listen_rec *lr = NULL;
int have_idle_worker = 0;
int last_poll_idx = 0;
free(ti);
- rv = apr_pollset_create(&pollset, num_listensocks, tpool,
- APR_POLLSET_NOCOPY);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "Couldn't create pollset in thread;"
- " check system or user limits");
- /* let the parent decide how bad this really is */
- clean_child_exit(APEXIT_CHILDSICK);
- }
-
- for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) {
- apr_pollfd_t *pfd = apr_pcalloc(tpool, sizeof *pfd);
-
- pfd->desc_type = APR_POLL_SOCKET;
- pfd->desc.s = lr->sd;
- pfd->reqevents = APR_POLLIN;
- pfd->client_data = lr;
-
- rv = apr_pollset_add(pollset, pfd);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "Couldn't create add listener to pollset;"
- " check system or user limits");
- /* let the parent decide how bad this really is */
- clean_child_exit(APEXIT_CHILDSICK);
- }
-
- lr->accept_func = ap_unixd_accept;
- }
-
/* Unblock the signal used to wake this thread up, and set a handler for
* it.
*/
apr_int32_t numdesc;
const apr_pollfd_t *pdesc;
- rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc);
+ rv = apr_pollset_poll(worker_pollset, -1, &numdesc, &pdesc);
if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_EINTR(rv)) {
continue;
my_info->tid = -1; /* listener thread doesn't have a thread slot */
my_info->sd = 0;
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(00275)
"apr_thread_create: unable to create listener thread");
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;
+ ap_listen_rec *lr;
apr_status_t rv;
- int i;
- int threads_created = 0;
- int listener_started = 0;
- int loops;
- int prev_threads_created;
+
+ /* 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(03140)
"ap_queue_create() failed");
clean_child_exit(APEXIT_CHILDFATAL);
}
- rv = ap_queue_info_create(&worker_queue_info, pchild,
+ rv = ap_queue_info_create(&worker_queue_info, pruntime,
threads_per_child, -1);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03141)
clean_child_exit(APEXIT_CHILDFATAL);
}
- worker_sockets = apr_pcalloc(pchild, threads_per_child
- * sizeof(apr_socket_t *));
+ /* Create the main pollset */
+ rv = apr_pollset_create(&worker_pollset, num_listensocks, pruntime,
+ APR_POLLSET_NOCOPY);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(03285)
+ "Couldn't create pollset in thread;"
+ " check system or user limits");
+ /* let the parent decide how bad this really is */
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
+
+ for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) {
+ apr_pollfd_t *pfd = apr_pcalloc(pruntime, sizeof *pfd);
+
+ pfd->desc_type = APR_POLL_SOCKET;
+ pfd->desc.s = lr->sd;
+ pfd->reqevents = APR_POLLIN;
+ pfd->client_data = lr;
+
+ rv = apr_pollset_add(worker_pollset, pfd);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(03286)
+ "Couldn't create add listener to pollset;"
+ " check system or user limits");
+ /* let the parent decide how bad this really is */
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
+
+ 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;
loops = prev_threads_created = 0;
while (1) {
* 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(03142)
"apr_thread_create: unable to create worker thread");
ap_my_pid = getpid();
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++) {
requests_this_child = INT_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