This doesn't work currently. It is posted just in case somebody takes
a look and finds something they disagree with or can offer hints. It
will serve pages but graceful restart doesn't terminate the old server
process properly and I haven't tried any other interesting scenarios.
The next thing I need to do is to add APR_HAS_THREADS tweaks to the
pod support to avoid checking the pod more than once in a given
process once some thread in the process finds out that it should die.
For this we need to set up an intra-process mutex in each server
process. ap_mpm_pod_child_open() may be necessary to do that step. I
don't know how cool it is to inherit a mutex from the parent.
Index: server/mpm/threaded/threaded.c
===================================================================
RCS file: /home/cvs/httpd-2.0/server/mpm/threaded/threaded.c,v
retrieving revision 1.35
diff -u -r1.35 threaded.c
--- server/mpm/threaded/threaded.c 2001/06/15 18:33:09 1.35
+++ server/mpm/threaded/threaded.c 2001/06/19 17:55:16
@@ -131,10 +131,7 @@
char ap_coredump_dir[MAX_STRING_LEN];
-static apr_file_t *pipe_of_death_in = NULL;
-static apr_file_t *pipe_of_death_out = NULL;
-static apr_lock_t *pipe_of_death_mutex; /* insures that a child process only
- consumes one character */
+static ap_pod_t *pod;
/* *Non*-shared http_main globals... */
@@ -170,10 +167,80 @@
static apr_lock_t *accept_mutex;
static const char *lock_fname;
-#ifdef NO_SERIALIZED_ACCEPT
-#define SAFE_ACCEPT(stmt) APR_SUCCESS
+/* a clean exit from a child with proper cleanup */
+static void clean_child_exit(int code) __attribute__ ((noreturn));
+static void clean_child_exit(int code)
+{
+ if (pchild) {
+ apr_pool_destroy(pchild);
+ }
+ exit(code);
+}
+
+static void accept_mutex_child_init(apr_pool_t *p)
+{
+ apr_status_t rv;
+
+ rv = apr_lock_child_init(&accept_mutex, lock_fname, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "Couldn't initialize cross-process lock in child");
+ clean_child_exit(APEXIT_CHILDINIT);
+ }
+}
+
+static apr_status_t accept_mutex_init(apr_pool_t *p)
+{
+ apr_status_t rv;
+
+ /* Initialize cross-process accept lock */
+ lock_fname = apr_psprintf(p, "%s.%" APR_OS_PROC_T_FMT,
+ ap_server_root_relative(p, lock_fname),
+ ap_my_pid);
+ rv = apr_lock_create(&accept_mutex, APR_MUTEX, APR_LOCKALL,
+ lock_fname, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "Couldn't create accept lock");
+ }
+ return rv;
+}
+
+static void accept_mutex_on(void)
+{
+ apr_status_t rv;
+
+ rv = apr_lock_acquire(accept_mutex);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "apr_lock_acquire failed. Attempting to shutdown "
+ "process gracefully.");
+ workers_may_exit = 1;
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ apr_status_t rv;
+
+ rv = apr_lock_release(accept_mutex);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "apr_lock_release failed. Attempting to shutdown "
+ "process gracefully.");
+ workers_may_exit = 1;
+ }
+}
+
+/* On some architectures it's safe to do unserialized accept()s in the single
+ * Listen case. But it's never safe to do it in the case where there's
+ * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ * when it's safe in the single Listen case.
+ */
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0)
#else
-#define SAFE_ACCEPT(stmt) (stmt)
+#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
#endif
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
@@ -201,16 +268,6 @@
return APR_ENOTIMPL;
}
-/* a clean exit from a child with proper cleanup */
-static void clean_child_exit(int code) __attribute__ ((noreturn));
-static void clean_child_exit(int code)
-{
- if (pchild) {
- apr_pool_destroy(pchild);
- }
- exit(code);
-}
-
/* handle all varieties of core dumping signals */
static void sig_coredump(int sig)
{
@@ -466,29 +523,6 @@
}
}
-/* Sets workers_may_exit if we received a character on the pipe_of_death */
-static void check_pipe_of_death(void)
-{
- apr_lock_acquire(pipe_of_death_mutex);
- if (!workers_may_exit) {
- apr_status_t ret;
- char pipe_read_char;
- apr_size_t n = 1;
-
- ret = apr_recv(listensocks[0], &pipe_read_char, &n);
- if (APR_STATUS_IS_EAGAIN(ret)) {
- /* It lost the lottery. It must continue to suffer
- * through a life of servitude. */
- }
- else {
- /* It won the lottery (or something else is very
- * wrong). Embrace death with open arms. */
- workers_may_exit = 1;
- }
- }
- apr_lock_release(pipe_of_death_mutex);
-}
-
static void * worker_thread(void * dummy)
{
proc_info * ti = dummy;
@@ -511,8 +545,9 @@
worker_thread_count++;
apr_lock_release(worker_thread_count_mutex);
- apr_poll_setup(&pollset, num_listensocks+1, tpool);
- for(n=0 ; n <= num_listensocks ; ++n)
+ /* XXX noop with SAFE_ACCEPT() */
+ apr_poll_setup(&pollset, num_listensocks, tpool);
+ for (n = 0 ; n < num_listensocks; ++n)
apr_poll_socket_add(pollset, listensocks[n], APR_POLLIN);
/* TODO: Switch to a system where threads reuse the results from earlier
@@ -525,46 +560,27 @@
(void) ap_update_child_status(process_slot, thread_slot, SERVER_READY,
(request_rec *) NULL);
- if ((rv = SAFE_ACCEPT(apr_lock_acquire(accept_mutex)))
- != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "apr_lock_acquire failed. Attempting to shutdown "
- "process gracefully.");
- workers_may_exit = 1;
- }
+ SAFE_ACCEPT(accept_mutex_on());
while (!workers_may_exit) {
apr_status_t ret;
apr_int16_t event;
- ret = apr_poll(pollset, &n, -1);
- if (ret != APR_SUCCESS) {
- if (APR_STATUS_IS_EINTR(ret)) {
- continue;
- }
+ if (ap_listeners->next) {
+ /* more than one socket */
+ ret = apr_poll(pollset, &n, -1);
+ if (ret != APR_SUCCESS) {
+ if (APR_STATUS_IS_EINTR(ret)) {
+ continue;
+ }
- /* apr_poll() will only return errors in catastrophic
- * circumstances. Let's try exiting gracefully, for now. */
- ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
+ /* apr_poll() will only return errors in catastrophic
+ * circumstances. Let's try exiting gracefully, for now. */
+ ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
ap_server_conf, "apr_poll: (listen)");
- workers_may_exit = 1;
- }
-
- if (workers_may_exit) break;
-
- apr_poll_revents_get(&event, listensocks[0], pollset);
- if (event & APR_POLLIN) {
- /* A process got a signal on the shutdown pipe. Check if we're
- * the lucky process to die. */
- check_pipe_of_death();
- continue;
- }
+ workers_may_exit = 1;
+ }
- if (num_listensocks == 1) {
- sd = ap_listeners->sd;
- goto got_fd;
- }
- else {
/* find a listener */
curr_pollfd = last_pollfd;
do {
@@ -581,6 +597,11 @@
}
} while (curr_pollfd != last_pollfd);
}
+ else {
+ /* only one socket, just pretend we did the other stuff */
+ sd = ap_listeners->sd;
+ goto got_fd;
+ }
}
got_fd:
if (!workers_may_exit) {
@@ -589,26 +610,17 @@
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
"apr_accept");
}
- if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
- != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "apr_lock_release failed. Attempting to shutdown "
- "process gracefully.");
+ SAFE_ACCEPT(accept_mutex_off());
+ if (!ap_mpm_pod_check(pod)) {
workers_may_exit = 1;
}
- if (csd != NULL) {
+ else if (csd != NULL) {
process_socket(ptrans, csd, process_slot, thread_slot);
requests_this_child--;
}
}
else {
- if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex)))
- != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "apr_lock_release failed. Attempting to shutdown "
- "process gracefully.");
- workers_may_exit = 1;
- }
+ SAFE_ACCEPT(accept_mutex_off());
break;
}
apr_pool_clear(ptrans);
@@ -649,21 +661,17 @@
ap_listen_rec *lr;
apr_status_t rv;
-
ap_my_pid = getpid();
+
+ /* Get a subpool for global allocations in this child so that we
+ * can have cleanups occur when the child exits.
+ */
apr_pool_create(&pchild, pconf);
/*stuff to do before we switch id's, so we have permissions.*/
reopen_scoreboard(pchild);
+ SAFE_ACCEPT(accept_mutex_child_init(pchild));
- rv = SAFE_ACCEPT(apr_lock_child_init(&accept_mutex, lock_fname,
- pchild));
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
- "Couldn't initialize cross-process lock in child");
- clean_child_exit(APEXIT_CHILDFATAL);
- }
-
if (unixd_setup_child()) {
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -689,12 +697,9 @@
/* Set up the pollfd array */
listensocks = apr_pcalloc(pchild,
- sizeof(*listensocks) * (num_listensocks + 1));
-#if APR_FILES_AS_SOCKETS
- apr_socket_from_file(&listensocks[0], pipe_of_death_in);
-#endif
- for (lr = ap_listeners, i = 1; i <= num_listensocks; lr = lr->next, ++i)
- listensocks[i]=lr->sd;
+ sizeof(*listensocks) * num_listensocks);
+ for (lr = ap_listeners, i = 0; i < num_listensocks; lr = lr->next, ++i)
+ listensocks[i] = lr->sd;
/* Setup worker threads */
@@ -707,8 +712,6 @@
worker_thread_count = 0;
apr_lock_create(&worker_thread_count_mutex, APR_MUTEX, APR_INTRAPROCESS,
NULL, pchild);
- apr_lock_create(&pipe_of_death_mutex, APR_MUTEX, APR_INTRAPROCESS,
- NULL, pchild);
apr_threadattr_create(&thread_attr, pchild);
apr_threadattr_detach_set(thread_attr, 0); /* 0 means PTHREAD_CREATE_JOINABLE
*/
@@ -828,29 +831,6 @@
return 0;
}
-/* If there aren't many connections coming in from the network, the child
- * processes may need to be awakened from their network i/o waits.
- * The pipe of death is an effective prod.
- */
-
-static void wake_up_and_die(void)
-{
- int i;
- char char_of_death = '!';
- apr_size_t one = 1;
- apr_status_t rv;
-
- for (i = 0; i < ap_daemons_limit;) {
- if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one))
- != APR_SUCCESS) {
- if (APR_STATUS_IS_EINTR(rv)) continue;
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
- "write pipe_of_death");
- }
- i++;
- }
-}
-
/* start up a bunch of children */
static void startup_children(int number_to_start)
{
@@ -889,8 +869,6 @@
int free_slots[MAX_SPAWN_RATE];
int last_non_dead;
int total_non_dead;
- apr_size_t one = 1;
- apr_status_t rv;
/* initialize the free_list */
free_length = 0;
@@ -943,11 +921,11 @@
ap_max_daemons_limit = last_non_dead + 1;
if (idle_thread_count > max_spare_threads) {
- /* Kill off one child */
- char char_of_death = '!';
- if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) !=
APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, "write
pipe_of_death");
- }
+ /* Kill off one child... we use the pod because that'll cause it to
+ * shut down gracefully, in case it happened to pick up a request
+ * while we were counting
+ */
+ ap_mpm_pod_signal(pod);
idle_spawn_rate = 1;
}
else if (idle_thread_count < min_spare_threads) {
@@ -1067,21 +1045,7 @@
pconf = _pconf;
ap_server_conf = s;
- rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, pconf);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv,
- (const server_rec*) ap_server_conf,
- "apr_file_pipe_create (pipe_of_death)");
- exit(1);
- }
- if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv,
- (const server_rec*) ap_server_conf,
- "apr_file_pipe_timeout_set (pipe_of_death)");
- exit(1);
- }
- ap_server_conf = s;
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
/* XXX: hey, what's the right way for the mpm to indicate a fatal error? */
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
@@ -1090,18 +1054,16 @@
}
ap_log_pid(pconf, ap_pid_fname);
- /* Initialize cross-process accept lock */
- lock_fname = apr_psprintf(_pconf, "%s.%" APR_OS_PROC_T_FMT,
- ap_server_root_relative(_pconf, lock_fname),
- ap_my_pid);
- rv = apr_lock_create(&accept_mutex, APR_MUTEX, APR_LOCKALL,
- lock_fname, _pconf);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't create accept lock");
+ if ((rv = ap_mpm_pod_open(pconf, &pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+ "Could not open pipe-of-death.");
return 1;
}
+ SAFE_ACCEPT(rv = accept_mutex_init(pconf));
+ if (rv != APR_SUCCESS) {
+ return 1;
+ }
if (!is_graceful) {
ap_create_scoreboard(pconf, SB_SHARED);
@@ -1147,7 +1109,7 @@
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
- wake_up_and_die();
+ /* wake_up_and_die(); er, this wrote to the POD... why do that *and* send
+SIGTERM? */
if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg
SIGTERM");
@@ -1188,13 +1150,16 @@
update_scoreboard_global();
/* wake up the children...time to die. But we'll have more soon */
- wake_up_and_die();
+ /* wake_up_and_die(); er, this wrote to the pod... why do that *and* send
+SIGTERM? */
if (is_graceful) {
int i, j;
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
"SIGWINCH received. Doing graceful restart");
+
+ /* kill off the server processes gracefully */
+ ap_mpm_pod_killpg(pod, ap_daemons_limit);
/* This is mostly for debugging... so that we know what is still
* gracefully dealing with existing request.
--
Jeff Trawick | [EMAIL PROTECTED] | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...