httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From <...@covalent.net>
Subject [PATCH] The threaded MPM graceful patch
Date Sat, 23 Jun 2001 07:31:36 GMT

It took a bit more re-working to make everything work, but this does seem
to solve the general problem.  To test this, I started the server,
connected without making a request, and sent a graceful restart signal.
Then, I checked the ps output, and finished the request.

Basically, the server shutdown the first process and started a second
while still holding onto the open connection.  When the request was
finished, the original process died cleanly.

The patch has 90 lines of additional code, but removes 53 because most of
the changes are just moving code around.  I'll be off-line most of
tomorrow, but I'll be back sometime tomorrow night.

Comments?

Ryan

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 -d -b -w -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/23 07:20:21
@@ -109,6 +109,7 @@
 static int min_spare_threads=0;
 static int max_spare_threads=0;
 static int ap_daemons_limit=0;
+static int dying = 0;
 static int workers_may_exit = 0;
 static int requests_this_child;
 static int num_listensocks = 0;
@@ -122,6 +123,15 @@
     apr_pool_t *tpool; /* "pthread" would be confusing */
 } proc_info;

+/* Structure used to pass information to the thread responsible for
+ * creating the rest of the threads.
+ */
+typedef struct {
+    apr_thread_t **threads;
+    int child_num_arg;
+    apr_threadattr_t *threadattr;
+} thread_starter;
+
 /*
  * The max child slot ever assigned, preserved across restarts.  Necessary
  * to deal with MaxClients changes across SIGWINCH restarts.  We use this
@@ -615,8 +625,9 @@
     }

     apr_pool_destroy(tpool);
-    ap_update_child_status(process_slot, thread_slot, SERVER_DEAD,
+    ap_update_child_status(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL,
         (request_rec *) NULL);
+    dying = 1;
     apr_lock_acquire(worker_thread_count_mutex);
     worker_thread_count--;
     if (worker_thread_count == 0) {
@@ -639,17 +650,81 @@
     return 0;
 }

-static void child_main(int child_num_arg)
+static void *start_threads(void * dummy)
 {
-    apr_thread_t **threads;
-    apr_threadattr_t *thread_attr;
+    thread_starter *ts = dummy;
+    apr_thread_t **threads = ts->threads;
+    apr_threadattr_t *thread_attr = ts->threadattr;
+    int child_num_arg = ts->child_num_arg;
     int i;
     int my_child_num = child_num_arg;
     proc_info *my_info = NULL;
     ap_listen_rec *lr;
     apr_status_t rv;
+    int threads_created = 0;

+    while (1) {
+        for (i=0; i < ap_threads_per_child; i++) {
+            int status = ap_scoreboard_image->servers[child_num_arg][i].status;
+
+            if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
+                continue;
+            }

+   	    my_info = (proc_info *)malloc(sizeof(proc_info));
+            if (my_info == NULL) {
+                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
+		             "malloc: out of memory");
+                clean_child_exit(APEXIT_CHILDFATAL);
+            }
+	    my_info->pid = my_child_num;
+            my_info->tid = i;
+	    my_info->sd = 0;
+	    apr_pool_create(&my_info->tpool, pchild);
+
+  	    /* We are creating threads right now */
+	    (void) ap_update_child_status(my_child_num, i, SERVER_STARTING,
+	  			          (request_rec *) NULL);
+	    /* We let each thread update it's own scoreboard entry.  This is
+             * done because it let's us deal with tid better.
+	     */
+	    if ((rv = apr_thread_create(&threads[i], thread_attr, worker_thread, my_info, pchild)))
{
+	        ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+			     "apr_thread_create: unable to create worker thread");
+                /* In case system resources are maxxed out, we don't want
+                   Apache running away with the CPU trying to fork over and
+                   over and over again if we exit. */
+                sleep(10);
+	        clean_child_exit(APEXIT_CHILDFATAL);
+	    }
+            threads_created++;
+        }
+        if (workers_may_exit || threads_created == ap_threads_per_child) {
+            break;
+        }
+        sleep(1);
+    }
+
+    /* What state should this child_main process be listed as in the scoreboard...?
+     *  ap_update_child_status(my_child_num, i, SERVER_STARTING, (request_rec *) NULL);
+     *
+     *  This state should be listed separately in the scoreboard, in some kind
+     *  of process_status, not mixed in with the worker threads' status.
+     *  "life_status" is almost right, but it's in the worker's structure, and
+     *  the name could be clearer.   gla
+     */
+}
+
+static void child_main(int child_num_arg)
+{
+    apr_thread_t **threads;
+    int i;
+    proc_info *my_info = NULL;
+    ap_listen_rec *lr;
+    apr_status_t rv;
+    thread_starter *ts;
+    apr_threadattr_t *thread_attr;
+
     ap_my_pid = getpid();
     apr_pool_create(&pchild, pconf);

@@ -709,26 +784,17 @@
                     NULL, pchild);
     apr_lock_create(&pipe_of_death_mutex, APR_MUTEX, APR_INTRAPROCESS,
                     NULL, pchild);
+
+    ts = apr_palloc(pchild, sizeof(*ts));
+
     apr_threadattr_create(&thread_attr, pchild);
     apr_threadattr_detach_set(thread_attr, 0);    /* 0 means PTHREAD_CREATE_JOINABLE */
-
-    for (i=0; i < ap_threads_per_child; i++) {

-	my_info = (proc_info *)malloc(sizeof(proc_info));
-        if (my_info == NULL) {
-            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
-		         "malloc: out of memory");
-            clean_child_exit(APEXIT_CHILDFATAL);
-        }
-	my_info->pid = my_child_num;
-        my_info->tid = i;
-	my_info->sd = 0;
-	apr_pool_create(&my_info->tpool, pchild);
+    ts->threads = threads;
+    ts->child_num_arg = child_num_arg;
+    ts->threadattr = thread_attr;

-	/* We are creating threads right now */
-	(void) ap_update_child_status(my_child_num, i, SERVER_STARTING,
-				      (request_rec *) NULL);
-	if ((rv = apr_thread_create(&threads[i], thread_attr, worker_thread, my_info, pchild)))
{
+    if ((rv = apr_thread_create(&threads[i], thread_attr, start_threads, ts, pchild)))
{
 	    ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
 			 "apr_thread_create: unable to create worker thread");
             /* In case system resources are maxxed out, we don't want
@@ -737,19 +803,6 @@
             sleep(10);
 	    clean_child_exit(APEXIT_CHILDFATAL);
 	}
-	/* We let each thread update it's own scoreboard entry.  This is done
-	 * because it let's us deal with tid better.
-	 */
-    }
-
-    /* What state should this child_main process be listed as in the scoreboard...?
-     *  ap_update_child_status(my_child_num, i, SERVER_STARTING, (request_rec *) NULL);
-     *
-     *  This state should be listed separately in the scoreboard, in some kind
-     *  of process_status, not mixed in with the worker threads' status.
-     *  "life_status" is almost right, but it's in the worker's structure, and
-     *  the name could be clearer.   gla
-     */

     apr_signal_thread(check_signal);

@@ -785,10 +838,6 @@
 	child_main(slot);
     }

-    /* Tag this slot as occupied so that perform_idle_server_maintenance
-     * doesn't try to steal it */
-    (void) ap_update_child_status(slot, 0, SERVER_STARTING, (request_rec *) NULL);
-
     if ((pid = fork()) == -1) {
         ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process");

@@ -905,7 +954,6 @@
 	 * that ap_threads_per_child is always > 0 */
 	int status = SERVER_DEAD;
 	int any_dying_threads = 0;
-	int all_dead_threads = 1;
 	int idle_thread_addition = 0;

 	if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate)
@@ -914,9 +962,7 @@
             ws = &ap_scoreboard_image->servers[i][j];
 	    status = ws->status;

-	    any_dying_threads = any_dying_threads || (status == SERVER_DEAD)
-                                    || (status == SERVER_GRACEFUL);
-	    all_dead_threads = all_dead_threads && (status == SERVER_DEAD);
+	    any_dying_threads = any_dying_threads || (status == SERVER_GRACEFUL);

 	    /* We consider a starting server as idle because we started it
 	     * at least a cycle ago, and if it still hasn't finished starting
@@ -928,14 +974,12 @@
 	        ++idle_thread_addition;
 	    }
 	}
-	if (all_dead_threads && free_length < idle_spawn_rate) {
+	if (any_dying_threads && free_length < idle_spawn_rate) {
 	    free_slots[free_length] = i;
 	    ++free_length;
 	}
-	if (!all_dead_threads) {
-            last_non_dead = i;
-	}
         if (!any_dying_threads) {
+            last_non_dead = i;
             ++total_non_dead;
 	    idle_thread_count += idle_thread_addition;
         }
@@ -1200,13 +1244,6 @@
          * gracefully dealing with existing request.
          */

-	for (i = 0; i < ap_daemons_limit; ++i) {
-  	    for (j = 0; j < ap_threads_per_child; j++) {
-	        if (ap_scoreboard_image->servers[i][j].status != SERVER_DEAD) {
-		    ap_scoreboard_image->servers[i][j].status = SERVER_GRACEFUL;
-		}
-	    }
-	}
     }
     else {
       /* Kill 'em all.  Since the child acts the same on the parents SIGTERM


_______________________________________________________________________________
Ryan Bloom                        	rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------


Mime
View raw message