With the level of code in CVS, SIGTERM wakes up the main thread in
each child. The main thread then enters pthread_join() to wait for a
worker thread to complete but nothing ever happens because there is
nothing to wake up a worker thread.
This patch moves the workers_may_exit field to the scoreboard and has
the parent process set this field and bombard the pipe of death before
sending SIGTERM to the child processes.
The workers_may_exit field is Paul's new life_status field. Since
this field applies to the entire child process it was moved to a
different place in the scoreboard and prefork.c was updated as
appropriate.
Try to ignore the mpm_trace() stuff in the patch...
Anything good in the following patch is from Greg Ames. The rest is
mine.
Comments?
Index: include/scoreboard.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/include/scoreboard.h,v
retrieving revision 1.17
diff -u -r1.17 scoreboard.h
--- include/scoreboard.h 2001/04/23 23:14:35 1.17
+++ include/scoreboard.h 2001/04/24 14:13:05
@@ -1,4 +1,3 @@
-
/* ====================================================================
* The Apache Software License, Version 1.1
*
@@ -152,6 +151,9 @@
#define SB_WORKING 0 /* The server is busy and the child is useful. */
#define SB_IDLE_DIE 1 /* The server is idle and the child is superfluous. */
/* The child should check for this and exit gracefully. */
+#define SB_TERM_DIE 2 /* The child is going away on the whim of the parent. */
+#define SB_MAXREQ_DIE 3 /* This child has served enough requests. */
+#define SB_FATAL_DIE 4 /* Some fatal error has occurred. */
/* stuff which is thread/process specific */
typedef struct {
@@ -170,7 +172,6 @@
unsigned long my_bytes_served;
unsigned long conn_bytes;
unsigned short conn_count;
- unsigned short life_status; /* Either SB_WORKING or SB_IDLE_DIE */
apr_time_t start_time;
apr_time_t stop_time;
#ifdef HAVE_TIMES
@@ -191,11 +192,15 @@
* should still be serving requests. */
} global_score;
-/* stuff which the parent generally writes and the children rarely read */
+/* stuff which (mostly) the parent generally writes and the children rarely read
+ * exception: life_status is written to by both parent and children and is
+ * checked often by children
+ */
typedef struct {
pid_t pid;
ap_generation_t generation; /* generation of this child */
int worker_threads;
+ unsigned short life_status; /* Either SB_WORKING or SB_IDLE_DIE */
#ifdef OPTIMIZE_TIMEOUTS
time_t last_rtime; /* time(0) of the last change */
vtime_t last_vtime; /* the last vtime the parent has seen */
Index: server/mpm/prefork/prefork.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/server/mpm/prefork/prefork.c,v
retrieving revision 1.173
diff -u -r1.173 prefork.c
--- server/mpm/prefork/prefork.c 2001/04/13 19:00:38 1.173
+++ server/mpm/prefork/prefork.c 2001/04/24 14:13:10
@@ -232,7 +232,7 @@
if (pchild) {
apr_pool_destroy(pchild);
}
- ap_scoreboard_image->servers[my_child_num][0].life_status = SB_WORKING;
+ ap_scoreboard_image->parent[my_child_num].life_status = SB_WORKING;
chdir_for_gprof();
exit(code);
}
@@ -373,7 +373,7 @@
static void please_die_gracefully(int sig)
{
/* clean_child_exit(0); */
- ap_scoreboard_image->servers[my_child_num][0].life_status = SB_IDLE_DIE;
+ ap_scoreboard_image->parent[my_child_num].life_status = SB_IDLE_DIE;
if (sig == SIGHUP) {
(void) ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num),
SERVER_GRACEFUL, (request_rec *) NULL);
@@ -529,7 +529,7 @@
static fd_set main_fds;
#define I_AM_TO_SHUTDOWN() \
-(ap_scoreboard_image->servers[my_child_num][0].life_status != SB_WORKING)
+(ap_scoreboard_image->parent[my_child_num].life_status != SB_WORKING)
int ap_graceful_stop_signalled(void)
{
@@ -840,7 +840,7 @@
apr_signal(SIGQUIT, SIG_DFL);
#endif
apr_signal(SIGTERM, please_die_gracefully);
- ap_scoreboard_image->servers[slot][0].life_status = SB_WORKING;
+ ap_scoreboard_image->parent[slot].life_status = SB_WORKING;
child_main(slot);
}
@@ -891,7 +891,7 @@
apr_signal(SIGHUP, please_die_gracefully);
apr_signal(SIGWINCH, please_die_gracefully);
apr_signal(SIGTERM, please_die_gracefully);
- ap_scoreboard_image->servers[slot][0].life_status = SB_WORKING;
+ ap_scoreboard_image->parent[slot].life_status = SB_WORKING;
child_main(slot);
}
@@ -1254,7 +1254,7 @@
update_scoreboard_global();
for (index = 0; index < ap_daemons_limit; ++index) {
- ap_scoreboard_image->servers[index][0].life_status = SB_IDLE_DIE;
+ ap_scoreboard_image->parent[index].life_status = SB_IDLE_DIE;
}
if (is_graceful) {
Index: server/mpm/threaded/threaded.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/server/mpm/threaded/threaded.c,v
retrieving revision 1.26
diff -u -r1.26 threaded.c
--- server/mpm/threaded/threaded.c 2001/04/23 23:14:35 1.26
+++ server/mpm/threaded/threaded.c 2001/04/24 14:13:13
@@ -105,11 +105,17 @@
static int min_spare_threads=0;
static int max_spare_threads=0;
static int ap_daemons_limit=0;
-static int workers_may_exit = 0;
static int requests_this_child;
static int num_listensocks = 0;
static apr_socket_t **listensocks;
+static int my_child_num;
+#define CHK_WORKERS_MAY_EXIT() \
+(ap_scoreboard_image->parent[my_child_num].life_status != SB_WORKING && \
+ mpm_trace("workers should exit now"))
+#define SET_WORKERS_MAY_EXIT(reason) \
+ap_scoreboard_image->parent[my_child_num].life_status = (reason)
+
/* The structure used to pass unique initialization info to each thread */
typedef struct {
int pid;
@@ -171,6 +177,13 @@
#define SAFE_ACCEPT(stmt) (stmt)
#endif
+static int mpm_trace(const char *s)
+{
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, ap_server_conf, "thread %d/%ld: %s",
+ ap_my_pid, (long)pthread_self(), s);
+ return 1;
+}
+
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
{
switch(query_code){
@@ -284,6 +297,7 @@
static void sig_term(int sig)
{
+ mpm_trace("got SIGTERM");
ap_start_shutdown();
}
@@ -435,11 +449,12 @@
ap_lingering_close(current_conn);
}
}
-/* Sets workers_may_exit if we received a character on the pipe_of_death */
+/* Sets WORKERS_MAY_EXIT if we received a character on the pipe_of_death */
static void check_pipe_of_death(void)
{
+ mpm_trace("got data on pipe of death");
apr_lock_acquire(pipe_of_death_mutex);
- if (!workers_may_exit) {
+ if (!CHK_WORKERS_MAY_EXIT()) {
apr_status_t ret;
char pipe_read_char;
apr_size_t n = 1;
@@ -452,7 +467,7 @@
else {
/* It won the lottery (or something else is very
* wrong). Embrace death with open arms. */
- workers_may_exit = 1;
+ SET_WORKERS_MAY_EXIT(SB_TERM_DIE);
}
}
apr_lock_release(pipe_of_death_mutex);
@@ -487,8 +502,9 @@
/* TODO: Switch to a system where threads reuse the results from earlier
poll calls - manoj */
while (1) {
- workers_may_exit |= (ap_max_requests_per_child != 0) && (requests_this_child
<= 0);
- if (workers_may_exit) break;
+ if ((ap_max_requests_per_child != 0) && (requests_this_child <= 0))
+ SET_WORKERS_MAY_EXIT(SB_MAXREQ_DIE);
+ if (CHK_WORKERS_MAY_EXIT()) break;
(void) ap_update_child_status(process_slot, thread_slot, SERVER_READY,
(request_rec *) NULL);
@@ -497,10 +513,10 @@
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
"apr_lock_acquire failed. Attempting to shutdown "
"process gracefully.");
- workers_may_exit = 1;
+ SET_WORKERS_MAY_EXIT(SB_FATAL_DIE);
}
- while (!workers_may_exit) {
+ while (!CHK_WORKERS_MAY_EXIT()) {
apr_status_t ret;
apr_int16_t event;
@@ -514,10 +530,10 @@
* 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;
+ SET_WORKERS_MAY_EXIT(SB_FATAL_DIE);
}
- if (workers_may_exit) break;
+ if (CHK_WORKERS_MAY_EXIT()) break;
apr_poll_revents_get(&event, listensocks[0], pollset);
if (event & APR_POLLIN) {
@@ -550,7 +566,7 @@
}
}
got_fd:
- if (!workers_may_exit) {
+ if (!CHK_WORKERS_MAY_EXIT()) {
if ((rv = apr_accept(&csd, sd, ptrans)) != APR_SUCCESS) {
csd = NULL;
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
@@ -561,7 +577,7 @@
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
"apr_lock_release failed. Attempting to shutdown "
"process gracefully.");
- workers_may_exit = 1;
+ SET_WORKERS_MAY_EXIT(SB_FATAL_DIE);
}
if (csd != NULL) {
process_socket(ptrans, csd, process_slot, thread_slot);
@@ -574,7 +590,7 @@
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
"apr_lock_release failed. Attempting to shutdown "
"process gracefully.");
- workers_may_exit = 1;
+ SET_WORKERS_MAY_EXIT(SB_FATAL_DIE);
}
break;
}
@@ -611,12 +627,11 @@
apr_thread_t **threads;
apr_threadattr_t *thread_attr;
int i;
- int my_child_num = child_num_arg;
proc_info *my_info = NULL;
ap_listen_rec *lr;
apr_status_t rv;
-
+ my_child_num = child_num_arg;
ap_my_pid = getpid();
apr_pool_create(&pchild, pconf);
@@ -1073,11 +1088,35 @@
restart_pending = shutdown_pending = 0;
server_main_loop(remaining_children_to_start);
-
+ mpm_trace("back from server_main_loop");
if (shutdown_pending) {
+ int i;
+ char char_of_death = '!';
+
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
+ /* sequence of operations:
+ * 1) set the workers_may_exit flag for each child process
+ * 2) write data on the pipe of death so that the worker threads
+ * are awakened
+ * 3) send SIGTERM to the main threads (blocked in sigwait()) so they
+ * wake up and join up with the exited worker threads
+ */
+ mpm_trace("setting workers_may_exit");
+ for (i = 0; i < ap_daemons_limit; ++i) {
+ ap_scoreboard_image->parent[i].life_status = SB_TERM_DIE;
+ }
+ /* give the children the signal to die */
+ mpm_trace("sending pipe of death signals");
+ 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++;
+ }
+ mpm_trace("sending SIGTERM");
if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg
SIGTERM");
}
--
Jeff Trawick | [EMAIL PROTECTED] | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...