You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by rb...@hyperreal.org on 1999/02/09 22:39:58 UTC

cvs commit: apache-apr/pthreads/src/main http_core.c http_main.c http_request.c

rbb         99/02/09 13:39:57

  Modified:    pthreads/src/include http_main.h httpd.h scoreboard.h
               pthreads/src/main http_core.c http_main.c http_request.c
  Log:
  Updated scoreboard logic for hybrid process/thread server.  Keeps track of
  thread status, instead of process status.  Spawning new processes and killing
  old ones is still not done using the scoreboard.  That is the next change to
  make.  All values needed for those decisions are in the scoreboard file now.
  
  Revision  Changes    Path
  1.3       +2 -2      apache-apr/pthreads/src/include/http_main.h
  
  Index: http_main.h
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/include/http_main.h,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- http_main.h	1999/02/07 06:29:22	1.2
  +++ http_main.h	1999/02/09 21:39:52	1.3
  @@ -117,8 +117,8 @@
   void ap_keepalive_timeout(char *, request_rec *);
   
   API_EXPORT(void) ap_child_terminate(request_rec *r);
  -int ap_update_child_status(int child_num, int status, request_rec *r);
  -void ap_time_process_request(int child_num, int status);
  +int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r);
  +void ap_time_process_request(int child_num, int thread_num, int status);
   unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x);
   API_EXPORT(int) ap_check_alarm(void);
   
  
  
  
  1.5       +12 -0     apache-apr/pthreads/src/include/httpd.h
  
  Index: httpd.h
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/include/httpd.h,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- httpd.h	1999/02/07 06:29:22	1.4
  +++ httpd.h	1999/02/09 21:39:52	1.5
  @@ -310,6 +310,17 @@
   #define HARD_SERVER_LIMIT 256
   #endif
   
  +/* Limit on the threads per process.  Clients will be locked out if more than
  + * this  * HARD_SERVER_LIMIT are needed.
  + *
  + * We keep this for one reason it keeps the size of the scoreboard file small
  + * enough that we can read the whole thing without worrying too much about
  + * the overhead.
  + */
  +#ifndef HARD_THREAD_LIMIT
  +#define HARD_THREAD_LIMIT 500
  +#endif
  +
   /*
    * Special Apache error codes. These are basically used
    *  in http_main.c so we can keep track of various errors.
  @@ -795,6 +806,7 @@
       /* Information about the connection itself */
   
       int child_num;		/* The number of the child handling conn_rec */
  +    int thread_num;             /* number of the thread handling conn_rec */
       BUFF *client;		/* Connection to the guy */
   
       /* Who is the client? */
  
  
  
  1.4       +7 -5      apache-apr/pthreads/src/include/scoreboard.h
  
  Index: scoreboard.h
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/include/scoreboard.h,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- scoreboard.h	1999/02/07 06:29:22	1.3
  +++ scoreboard.h	1999/02/09 21:39:52	1.4
  @@ -129,12 +129,13 @@
    */
   typedef int ap_generation_t;
   
  -/* stuff which the children generally write, and the parent mainly reads */
  +/* stuff which is thread specific */
   typedef struct {
   #ifdef OPTIMIZE_TIMEOUTS
       vtime_t cur_vtime;		/* the child's current vtime */
       unsigned short timeout_len;	/* length of the timeout */
   #endif
  +    pthread_t tid;
       unsigned char status;
       unsigned long access_count;
       unsigned long bytes_served;
  @@ -159,7 +160,7 @@
       char request[64];		/* We just want an idea... */
       server_rec *vhostrec;	/* What virtual host is being accessed? */
                                   /* SEE ABOVE FOR SAFE USAGE! */
  -} short_score;
  +} thread_score;
   
   typedef struct {
       ap_generation_t running_generation;	/* the generation of children which
  @@ -169,16 +170,17 @@
   /* stuff which the parent generally writes and the children rarely read */
   typedef struct {
       pid_t pid;
  -    pthread_t tid;
  +    ap_generation_t generation;	/* generation of this child */
  +    int threads;
  +    int inactive_threads;
   #ifdef OPTIMIZE_TIMEOUTS
       time_t last_rtime;		/* time(0) of the last change */
       vtime_t last_vtime;		/* the last vtime the parent has seen */
   #endif
  -    ap_generation_t generation;	/* generation of this child */
   } parent_score;
   
   typedef struct {
  -    short_score servers[HARD_SERVER_LIMIT];
  +    thread_score servers[HARD_SERVER_LIMIT][HARD_THREAD_LIMIT];
       parent_score parent[HARD_SERVER_LIMIT];
       global_score global;
   } scoreboard;
  
  
  
  1.4       +4 -4      apache-apr/pthreads/src/main/http_core.c
  
  Index: http_core.c
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/main/http_core.c,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- http_core.c	1999/02/07 06:29:30	1.3
  +++ http_core.c	1999/02/09 21:39:55	1.4
  @@ -595,8 +595,8 @@
   	&& conn->remote_host == NULL
   	&& (type == REMOTE_DOUBLE_REV
   	    || hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
  -	old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS,
  -					  (request_rec*)NULL);
  +	old_stat = ap_update_child_status(conn->child_num, conn->thread_num,
  +					  SERVER_BUSY_DNS, (request_rec*)NULL);
   	/* ZZZ change to AP functions. */
   	iaddr = &(conn->remote_addr.sin_addr);
   	hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET);
  @@ -623,8 +623,8 @@
   	}
       }
       if (old_stat != SERVER_DEAD) {
  -	(void)ap_update_child_status(conn->child_num, old_stat,
  -				     (request_rec*)NULL);
  +	(void)ap_update_child_status(conn->child_num, conn->thread_num,
  +				     old_stat, (request_rec*)NULL);
       }
   
   /*
  
  
  
  1.12      +250 -108  apache-apr/pthreads/src/main/http_main.c
  
  Index: http_main.c
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/main/http_main.c,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- http_main.c	1999/02/08 13:58:38	1.11
  +++ http_main.c	1999/02/09 21:39:56	1.12
  @@ -182,6 +182,15 @@
   static int max_daemons_limit = -1;
   
   /*
  + * The maximum number of threads in each child process.  This is ap_threads_
  + * per child + # of acceptor threads.  Used to optimize routines that must
  + * scan the entire scoreboard.  This is garaunteed not to change, unless the
  + * server is restarted, at which point, it must be updated to incorporate
  + * any changes in the threadsperchild or listen directives.
  + */
  +int max_threads_limit;
  +
  +/*
    * During config time, listeners is treated as a NULL-terminated list.
    * child_main previously would start at the beginning of the list each time
    * through the loop, so a socket early on in the list could easily starve out
  @@ -218,6 +227,14 @@
       int generation;
   } tls_main_t;
   
  +/* The info each server thread needs to start correctly.
  + */
  +typedef struct {
  +    int pid;
  +    int tid;
  +    int sd;
  +} proc_info;
  +
   static pthread_key_t tls_main_key;        /* ZZZZ */
   
   static tls_main_t *gettls(pthread_key_t tls_main_key)
  @@ -594,7 +611,7 @@
       /* Wait for readable data or error condition on socket;
        * slurp up any data that arrives...  We exit when we go for an
        * interval of tv length without getting any more data, get an error
  -     * from select(), get an error or EOF on a read, or the timer expires.
  +     * from poll(), get an error or EOF on a read, or the timer expires.
        */
       /* We use a 2 second timeout because current (Feb 97) browsers
        * fail to close a connection after the server closes it.  Thus,
  @@ -603,7 +620,7 @@
        * This should be sufficient unless the connection is massively
        * losing packets, in which case we might have missed the RST anyway.
        * These parameters are reset on each pass, since they might be
  -     * changed by select.
  +     * changed by poll.
        */
       do {
           pd.revents = 0;
  @@ -745,9 +762,42 @@
   
   static void reinit_scoreboard(pool *p)
   {
  -    ap_assert(!ap_scoreboard_image);
  -    ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE);
  +    int i;
  +    int running_gen = 0;
  +    if (ap_scoreboard_image)
  +        running_gen = ap_scoreboard_image->global.running_generation;
  +
  +#ifndef SCOREBOARD_FILE
  +    if (ap_scoreboard_image == NULL) {
  +	setup_shared_mem(p);
  +    }
       memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
  +    ap_scoreboard_image->global.running_generation = running_gen;
  +    
  +    for (i=0; i < HARD_SERVER_LIMIT; i++)
  +        ap_scoreboard_image->parent[i].threads = 
  +        ap_scoreboard_image->parent[i].threads = HARD_SERVER_LIMIT;
  +#else
  +    ap_scoreboard_image = &_scoreboard_image;
  +    ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
  +
  +    scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644);
  +    if (scoreboard_fd == -1) {
  +	perror(ap_scoreboard_fname);
  +	fprintf(stderr, "Cannot open scoreboard file:\n");
  +	exit(APEXIT_INIT);
  +    }
  +    ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup);
  +
  +    memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image));
  +    ap_scoreboard_image->global.running_generation = running_gen;
  +    
  +    for (i=0; i < HARD_SERVER_LIMIT; i++)
  +        ap_scoreboard_image->parent[i].threads = 
  +        ap_scoreboard_image->parent[i].threads = HARD_SERVER_LIMIT;
  +
  +    force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
  +#endif
   }
   
   API_EXPORT(int) ap_exists_scoreboard_image(void)
  @@ -764,18 +814,30 @@
       exit(code);
   }
   
  -int ap_update_child_status(int child_num, int status, request_rec *r)
  +int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r)
   {
       int old_status;
  -    short_score *ss;
  +    thread_score *ss;
  +    parent_score *ps;
   
       if (child_num < 0)
   	return -1;
   
  -    ss = &ap_scoreboard_image->servers[child_num];
  +    fprintf(stderr, "Updating status for %d %d to %d\n", child_num, thread_num, status);
  +
  +    ss = &ap_scoreboard_image->servers[child_num][thread_num];
       old_status = ss->status;
       ss->status = status;
   
  +    ps = &ap_scoreboard_image->parent[child_num];
  +    
  +    if (status == SERVER_READY && old_status != SERVER_READY)
  +        ps->inactive_threads++;
  +    else if (old_status == SERVER_READY)
  +        ps->inactive_threads--;
  +    else if (old_status == SERVER_STARTING)
  +        ss->tid = pthread_self();
  +
       if (ap_extended_status) {
   	if (status == SERVER_READY || status == SERVER_DEAD) {
   	    /*
  @@ -810,14 +872,14 @@
       return old_status;
   }
   
  -void ap_time_process_request(int child_num, int status)
  +void ap_time_process_request(int child_num, int thread_num, int status)
   {
  -    short_score *ss;
  +    thread_score *ss;
   
       if (child_num < 0)
   	return;
   
  -    ss = &ap_scoreboard_image->servers[child_num];
  +    ss = &ap_scoreboard_image->servers[child_num][thread_num];
   
       if (status == START_PREQUEST) {
         /*ss->start_time = GetCurrentTime(); ZZZ return time in uS since the 
  @@ -840,7 +902,7 @@
   static void increment_counts(int child_num, request_rec *r)
   {
       long int bs = 0;
  -    short_score *ss;
  +    thread_score *ss;
   
       ss = &ap_scoreboard_image->servers[child_num];
   
  @@ -859,12 +921,12 @@
   
   }
   
  -static int find_child_by_tid(pthread_t tid) /* ZZZ */
  +static int find_child_by_pid(pid_t pid) /* ZZZ */
   {
       int i;
   
       for (i = 0; i < max_daemons_limit; ++i)
  -	if (ap_scoreboard_image->parent[i].tid == tid)
  +	if (ap_scoreboard_image->parent[i].pid == pid)
   	    return i;
   
       return -1;
  @@ -964,7 +1026,7 @@
   static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout,
                                   const struct sockaddr_in *remaddr,   /* ZZZ */
                                   const struct sockaddr_in *saddr,     /* ZZZ */
  -			     int child_num)
  +			     int child_num, int thread_num)
   {
       conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec));
   
  @@ -973,6 +1035,7 @@
        */
   
       conn->child_num = child_num;
  +    conn->thread_num = thread_num;
   
       conn->pool = p;
       conn->local_addr = *saddr;
  @@ -1433,7 +1496,7 @@
       /* ZZZ doesn't really make sense in a threaded server. */
   }
   
  -static void process_socket(pool *ptrans, struct sockaddr *sa_client, int csd, int my_child_num) /* ZZZZ */
  +static void process_socket(pool *ptrans, struct sockaddr *sa_client, int csd, int my_child_num, int my_thread_num)
   {
       struct sockaddr sa_server; /* ZZZZ */
       size_t len = sizeof(struct sockaddr);
  @@ -1442,25 +1505,21 @@
       conn_rec *current_conn;
   
       ap_note_cleanups_for_fd(ptrans, csd);
  -    /*
  -     * We now have a connection, so set it up with the appropriate
  -     * socket options, file descriptors, and read/write buffers.
  -     */
  +
       /* ZZZ change to AP func */
       if (getsockname(csd, &sa_server, &len) < 0) { 
   	ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname");
   	return;
       }
  -    sock_disable_nagle(csd);
  -    (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
  -				(request_rec *) NULL);
  +    (void) ap_update_child_status(my_child_num, my_thread_num,  
  +				  SERVER_BUSY_READ, (request_rec *) NULL);
       conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
       ap_bpushfd(conn_io, csd, csd);
   
       current_conn = new_connection(ptrans, server_conf, conn_io,
                                     (const struct sockaddr_in *) sa_client, 
                                     (const struct sockaddr_in *) &sa_server, 
  -                                  my_child_num);
  +                                  my_child_num, my_thread_num);
       
       /*
        * Read and process each request found on our connection
  @@ -1470,7 +1529,8 @@
       fprintf(stderr,"%d child_main: about to read request\n",my_child_num);
   
       while ((r = ap_read_request(current_conn)) != NULL) {
  -	(void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r);
  +	(void) ap_update_child_status(my_child_num, my_thread_num,
  +				      SERVER_BUSY_WRITE, r);
   	fprintf(stderr,"%d child_main: about to process request\n",my_child_num);
   	if (r->status == HTTP_OK)
   	    ap_process_request(r);
  @@ -1480,8 +1540,9 @@
   	if (!current_conn->keepalive || ap_is_aborted(current_conn))
   	    break;
   	ap_destroy_pool(r->pool);
  -	(void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE,
  -				    (request_rec *) NULL);
  +	(void) ap_update_child_status(my_child_num, my_thread_num, 
  +				      SERVER_BUSY_KEEPALIVE, 
  +				      (request_rec *) NULL);
   #if 0
   	if (ap_scoreboard_image->global.exit_generation > t->generation) {
   	    ap_bclose(conn_io);
  @@ -1514,15 +1575,30 @@
   #endif
   }
   
  -void * accept_thread(void * sd)
  +void * accept_thread(void * dummy)
   {
  +    proc_info * ti = dummy;
  +    int my_pid = ti->pid;
  +    int my_tid = ti->tid;
  +    int sd = ti->sd;
       int csd = 0;
  -    int * temp = sd;
       struct sockaddr sa_client;
       size_t len = sizeof(struct sockaddr);
   
  +    free(ti);
  +
  +    /*
  +     * Let's setup the socket options on the master socket.  These
  +     * will be inherited by any dup'ed sockets for us.  No reason to do 
  +     * this oce for each request.
  +     */
  +    sock_disable_nagle(sd);
  +
  +    (void) ap_update_child_status(my_pid, my_tid, SERVER_READY, 
  +				  (request_rec *) NULL);
  +
       for (;;) {
  -        csd = accept(*temp, &sa_client, &len);
  +        csd = accept(sd, &sa_client, &len);
   	if (csd >= 0) {
   	    if (queue_push(&csd_queue, csd, &sa_client) != 0) {
   	      /*	        ap_log_error*/
  @@ -1536,13 +1612,20 @@
   
   void * worker_thread(void * dummy)
   {
  -    int my_pid = *(int *)dummy;
  +    proc_info * ti = dummy;
  +    int my_pid = ti->pid;
  +    int my_tid = ti->tid;
       struct sockaddr sa_client;
   
  +    free(ti);
  +
  +    (void) ap_update_child_status(my_pid, my_tid, SERVER_READY, 
  +				  (request_rec *) NULL);
  +
       for (;;) {
           int csd = queue_pop(&csd_queue, &sa_client);
   	pthread_t tid = pthread_self();
  -	process_socket(pchild, &sa_client, csd, my_pid);
  +	process_socket(pchild, &sa_client, csd, my_pid, my_tid);
       }
   }
   
  @@ -1672,11 +1755,11 @@
   static void child_main(int child_num_arg)
   {
       listen_rec *lr;
  -    int i;
  +    int i, curr;
       pthread_t thread;
       int my_child_num = child_num_arg;
  +    proc_info *my_info = NULL;
   
  -
       my_pid = getpid();
       requests_this_child = 0;
   
  @@ -1701,37 +1784,87 @@
       queue_init(&csd_queue, ap_threads_per_child);
   
       /* Setup worker threads */
  -   
       for (i=0; i < ap_threads_per_child; i++) {
  -      if (pthread_create(&thread, NULL, worker_thread, &my_child_num)) {
  +        my_info = NULL;
  +	
  +	my_info = (proc_info *)malloc(sizeof(proc_info));
  +	my_info->pid = my_child_num;
  +        my_info->tid = i;
  +	my_info->sd = 0;
  +	
  +	/* We are creating threads right now */
  +	(void) ap_update_child_status(my_child_num, i, SERVER_STARTING, 
  +				      (request_rec *) NULL);
  +	
  +	if (pthread_create(&thread, NULL, worker_thread, my_info)) {
   	    ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
   			 "pthread_create: unable to create worker thread");
  -	    exit(1);
  -	} else {
  -	  /*	UPDATE_SCOREBOARD with thread id and state */
  +	    /* 
  +	     * We failed to create a thread.  Update the scoreboard,  
  +	     * or it will say SERVER_STARTING forever.
  +	     */
  +	    (void) ap_update_child_status(my_child_num, i, SERVER_DEAD, 
  +					  (request_rec *) NULL);
  +	    exit(1);   /* We won't always exit here, but for no this is okay */
   	}
  +	/* We let each thread update it's own scoreboard entry.  This is done
  +	 * because it let's us deal with tid better.
  +	 */
       }    
       /* Setup acceptor threads */
  -
  +    
       lr = ap_listeners;
       while (lr->next != NULL) {
  -        if (pthread_create(&thread, NULL, accept_thread, &(lr->fd))) {
  -	    ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
  -			 "pthread_create: unable to create acceptor thread");
  -	    exit(1);
  +        my_info = NULL;
  +
  +	my_info = (proc_info *)malloc(sizeof(proc_info));
  +	my_info->pid = my_child_num;
  +        my_info->tid = i++;
  +	my_info->sd = lr->fd;
  +
  +	if (pthread_create(&thread, NULL, accept_thread, my_info)) {
  +	  ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
  +		       "pthread_create: unable to create acceptor thread");
  +	  /* 
  +	   * We failed to create a thread.  Update the scoreboard,  
  +	   * or it will say SERVER_STARTING forever.
  +	   */
  +	  exit(1);
   	} else {
  -	  /*	UPDATE_SCOREBOARD with thread id and state */
  +	  /* We let each thread update it's own scoreboard entry.  This is done
  +	   * because it let's us deal with tid better.
  +	   */
   	}
   	lr = lr->next;
       }
  -    accept_thread(&(lr->fd));
  +    my_info = NULL;
  +    
  +    my_info = (proc_info *)malloc(sizeof(proc_info));
  +    my_info->pid = my_child_num;
  +    my_info->tid = i++;
  +    my_info->sd = lr->fd;
  +    
  +    accept_thread(my_info);
   }
   
   static int make_child(server_rec *s, int slot, time_t now) /* ZZZ */
   {
       pthread_t tid;   /* ZZZZ */
       int pid;
  -    int rc;
  +    listen_rec *lr;
  +
  +    max_threads_limit = 0;
  +    lr = ap_listeners;
  +    for (lr = ap_listeners, max_threads_limit = 0; lr; 
  +	 lr=lr->next, max_threads_limit++);
  +
  +    max_threads_limit += ap_threads_per_child; 
  +    if (max_threads_limit  > HARD_THREAD_LIMIT) {
  +        ap_log_error(APLOG_MARK, APLOG_ERR, s,
  +		     "Worker threads plus acceptor threads is greater than HARD_THREAD_LIMIT, please correct");
  +	exit(-1);
  +    }
  +
   
       if (slot + 1 > max_daemons_limit) {
   	max_daemons_limit = slot + 1;
  @@ -1743,16 +1876,13 @@
       }
   
       Explain1("Starting new child in slot %d",slot);
  -    (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL);
  -
       if ((pid = fork()) == -1) {
           ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process");
           
   	/* fork didn't succeed. Fix the scoreboard or else
   	 * it will say SERVER_STARTING forever and ever
   	 */
  -	(void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL);
  -
  +	/*	(void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL);    We never put SERVER_STARTING in scoreboard */
   	/* 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. */
  @@ -1792,7 +1922,7 @@
       int i;
   
       for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
  -	if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
  +	if (ap_scoreboard_image->parent[i].pid != 0) {
   	    continue;
   	}
   	if (make_child(server_conf, i, 0) < 0) {
  @@ -1817,10 +1947,10 @@
   
   static void perform_idle_server_maintenance(void)
   {
  -    int i;
  +    int i, j;
       int to_kill;
  -    int idle_count;
  -    short_score *ss;
  +    int idle_count, idle_thread_count;
  +    thread_score *ss;
       time_t now = 0;
       int free_length;
       int free_slots[MAX_SPAWN_RATE];
  @@ -1837,91 +1967,98 @@
   
       for (i = 0; i < ap_daemons_limit; ++i) {
   	int status;
  +	idle_thread_count = 0;
   
   	if (i >= max_daemons_limit && free_length == idle_spawn_rate)
   	    break;
  -	ss = &ap_scoreboard_image->servers[i];
  -	status = ss->status;
  -	if (status == SERVER_DEAD) {
  -	    /* try to keep children numbers as low as possible */
  -	    if (free_length < idle_spawn_rate) {
  -		free_slots[free_length] = i;
  -		++free_length;
  +	for (j = 0; j < max_threads_limit; j++) {
  +            ss = &ap_scoreboard_image->servers[i][j];
  +	    status = ss->status;
  +	    if (status == SERVER_DEAD) {
  +	        /* try to keep children numbers as low as possible */
  +	        if (free_length < idle_spawn_rate) {
  +		    free_slots[free_length] = i;
  +		    ++free_length;
  +		    /* We are killing this process, let's leave the loop */
  +		    break;
  +		}
   	    }
  -	}
  -	else {
  -	    /* We consider a starting server as idle because we started it
  -	     * at least a cycle ago, and if it still hasn't finished starting
  -	     * then we're just going to swamp things worse by forking more.
  -	     * So we hopefully won't need to fork more if we count it.
  -	     * This depends on the ordering of SERVER_READY and SERVER_STARTING.
  -	     */
  -	    if (status <= SERVER_READY) {
  -		++ idle_count;
  -		/* always kill the highest numbered child if we have to...
  -		 * no really well thought out reason ... other than observing
  -		 * the server behaviour under linux where lower numbered children
  -		 * tend to service more hits (and hence are more likely to have
  -		 * their data in cpu caches).
  +	    else {
  +	        /* We consider a starting server as idle because we started it
  +		 * at least a cycle ago, and if it still hasn't finished starting
  +		 * then we're just going to swamp things worse by forking more.
  +		 * So we hopefully won't need to fork more if we count it.
  +		 * This depends on the ordering of SERVER_READY and SERVER_STARTING.
   		 */
  -		to_kill = i;
  +	        if (status <= SERVER_READY) {
  +	            ++ idle_thread_count;
  +		    /* always kill the highest numbered child if we have to...
  +		     * no really well thought out reason ... other than observing
  +		     * the server behaviour under linux where lower numbered children
  +		     * tend to service more hits (and hence are more likely to have
  +		     * their data in cpu caches).
  +		     */
  +		}
   	    }
  -
  -	    ++total_non_dead;
  -	    last_non_dead = i;
   	}
  +	if (idle_thread_count == max_threads_limit) {
  +	    idle_count++;
  +	    to_kill = i;
  +	}
  +	++total_non_dead;
  +	last_non_dead = i;
       }
       max_daemons_limit = last_non_dead + 1;
       if (idle_count > ap_daemons_max_free) {
  -	/* kill off one child... we use SIGUSR1 because that'll cause it to
  +        /* kill off one child... we use SIGUSR1 because that'll cause it to
   	 * shut down gracefully, in case it happened to pick up a request
   	 * while we were counting
   	 */
  -	/*kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1);*/
  -	idle_spawn_rate = 1;
  +        /*kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1);*/
  +        idle_spawn_rate = 1;
       }
       else if (idle_count < ap_daemons_min_free) {
  -	/* terminate the free list */
  -	if (free_length == 0) {
  +        /* terminate the free list */
  +        if (free_length == 0) {
   	    /* only report this condition once */
   	    static int reported = 0;
  -
  +	    
   	    if (!reported) {
  -		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
  -			    "server reached MaxClients setting, consider"
  -			    " raising the MaxClients setting");
  +	        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
  +			     "server reached MaxClients setting, consider"
  +			     " raising the MaxClients setting");
   		reported = 1;
   	    }
   	    idle_spawn_rate = 1;
   	}
   	else {
  -  	    /* ZZZZ */
  +	    /* ZZZZ */
   	    time_t now = 0; /*ZZZZZ */
  -
  +	    
   	    if (idle_spawn_rate >= 8) {
  -		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
  -		    "server seems busy, (you may need "
  -		    "to increase StartServers, or Min/MaxSpareServers), "
  -		    "spawning %d children, there are %d idle, and "
  -		    "%d total children", idle_spawn_rate,
  -		    idle_count, total_non_dead);
  +	        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
  +			     "server seems busy, (you may need "
  +			     "to increase StartServers, or Min/MaxSpareServers), "
  +			     "spawning %d children, there are %d idle, and "
  +			     "%d total children", idle_spawn_rate,
  +			     idle_count, total_non_dead);
   	    }
   	    for (i = 0; i < free_length; ++i) {
  -		make_child(server_conf, free_slots[i], now);
  +	        make_child(server_conf, free_slots[i], now);
   	    }
   	    /* the next time around we want to spawn twice as many if this
   	     * wasn't good enough, but not if we've just done a graceful
   	     */
   	    if (hold_off_on_exponential_spawning) {
  -		--hold_off_on_exponential_spawning;
  +	        --hold_off_on_exponential_spawning;
   	    }
   	    else if (idle_spawn_rate < MAX_SPAWN_RATE) {
  -		idle_spawn_rate *= 2;
  +	        idle_spawn_rate *= 2;
   	    }
   	}
       }
       else {
  -	idle_spawn_rate = 1;
  +      idle_spawn_rate = 1;
       }
   }
   
  @@ -1943,10 +2080,12 @@
           pid = -1;
           
           if (pid >= 0) {
  -            child_slot = find_child_by_tid(0);
  +            child_slot = find_child_by_pid(0);
               if (child_slot >= 0) {
  - 		(void) ap_update_child_status(child_slot, SERVER_DEAD,
  +	      /* 		(void) ap_update_child_status(child_slot, SERVER_DEAD,
   					    (request_rec *) NULL);
  +					    LOOK INTO THIS */
  +
   		if (remaining_children_to_start
   		    && child_slot < ap_daemons_limit) {
   		    /* we're still doing a 1-for-1 replacement of dead
  @@ -2084,7 +2223,7 @@
   #endif
   
       if (is_graceful) {
  -	int i;
  +	int i, j;
   
   	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
   		    "SIGUSR1 received.  Doing graceful restart");
  @@ -2098,10 +2237,13 @@
   	/* This is mostly for debugging... so that we know what is still
   	    * gracefully dealing with existing request.
   	    */
  +	
   	for (i = 0; i < ap_daemons_limit; ++i) {
  -	    if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
  -		ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL;
  -	    }
  +  	    for (j = 0; j < max_threads_limit; j++) { 
  +	        if (ap_scoreboard_image->servers[i][j].status != SERVER_DEAD) {
  +		    ap_scoreboard_image->servers[i][j].status = SERVER_GRACEFUL;
  +		}
  +	    } 
   	}
       }
       else {
  @@ -2124,7 +2266,7 @@
       is_graceful = 0;
   
       if (!one_process) {
  -	detach();
  +        detach();
       }
       else {
   	MONCONTROL(1);
  
  
  
  1.3       +8 -3      apache-apr/pthreads/src/main/http_request.c
  
  Index: http_request.c
  ===================================================================
  RCS file: /home/cvs/apache-apr/pthreads/src/main/http_request.c,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- http_request.c	1999/02/07 06:29:31	1.2
  +++ http_request.c	1999/02/09 21:39:56	1.3
  @@ -1221,11 +1221,13 @@
       int old_stat;
   
       if (ap_extended_status)
  -	ap_time_process_request(r->connection->child_num, START_PREQUEST);
  +	ap_time_process_request(r->connection->child_num, 
  +				r->connection->thread_num, START_PREQUEST);
   
       process_request_internal(r);
   
       old_stat = ap_update_child_status(r->connection->child_num,
  +				      r->connection->thread_num,
                                      SERVER_BUSY_LOG, r);
   
       /*
  @@ -1238,9 +1240,12 @@
       ap_bhalfduplex(r->connection->client);
       ap_log_transaction(r);
   
  -    (void) ap_update_child_status(r->connection->child_num, old_stat, r);
  +    (void) ap_update_child_status(r->connection->child_num, 
  +				  r->connection->thread_num, old_stat, r);
       if (ap_extended_status)
  -	ap_time_process_request(r->connection->child_num, STOP_PREQUEST);
  +	ap_time_process_request(r->connection->child_num,
  +				r->connection->thread_num, STOP_PREQUEST);
  +
   }
   
   static table *rename_original_env(pool *p, table *t)