You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by Dean Gaudet <dg...@hyperreal.com> on 1997/06/30 23:10:06 UTC

cvs commit: apache/src CHANGES http_main.c http_main.h httpd.h

dgaudet     97/06/30 14:10:05

  Modified:    src       CHANGES http_main.c http_main.h httpd.h
  Log:
  Unix scoreboard management revamp/cleanup.  Including a tweaked
  put_scoreboard_info from Harrie Hazewinkel's SNMP patch.  Fix starvation
  problem with multiple Listens and a busy socket.  Early versions of this
  patch were reviewed by Marc, Ben, Randy and Jim.
  
  Revision  Changes    Path
  1.313     +11 -1     apache/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /export/home/cvs/apache/src/CHANGES,v
  retrieving revision 1.312
  retrieving revision 1.313
  diff -C3 -r1.312 -r1.313
  *** CHANGES	1997/06/30 20:30:51	1.312
  --- CHANGES	1997/06/30 21:09:56	1.313
  ***************
  *** 1,5 ****
    Changes with Apache 1.3
  !   
      *) API: It's possible to replace standalone_main (define STANDALONE_MAIN)
         and it's possible to use SFIO for the underlying i/o layer.
         [Doug MacEachern]
  --- 1,15 ----
    Changes with Apache 1.3
  ! 
  !   *) Revamp of (unix) scoreboard management code such that it avoids
  !      unnecessary traversals of the scoreboard on each hit.  This is
  !      particularly important for high volume sites with a large
  !      HARD_SERVER_LIMIT.  Some of the previous operations were O(n^2),
  !      and are now O(n).  See also SCOREBOARD_MAINTENANCE_INTERVAL in
  !      httpd.h. [Dean Gaudet]
  ! 
  !   *) In configurations using multiple Listen statements it was possible for
  !      busy sockets to starve other sockets of service.  [Dean Gaudet]
  ! 
      *) API: It's possible to replace standalone_main (define STANDALONE_MAIN)
         and it's possible to use SFIO for the underlying i/o layer.
         [Doug MacEachern]
  
  
  
  1.172     +248 -197  apache/src/http_main.c
  
  Index: http_main.c
  ===================================================================
  RCS file: /export/home/cvs/apache/src/http_main.c,v
  retrieving revision 1.171
  retrieving revision 1.172
  diff -C3 -r1.171 -r1.172
  *** http_main.c	1997/06/30 20:28:51	1.171
  --- http_main.c	1997/06/30 21:09:58	1.172
  ***************
  *** 156,162 ****
    char *lock_fname;
    char *server_argv0;
    struct in_addr bind_address;
  - listen_rec *listeners;
    int daemons_to_start;
    int daemons_min_free;
    int daemons_max_free;
  --- 156,161 ----
  ***************
  *** 165,170 ****
  --- 164,196 ----
    int suexec_enabled = 0;
    int listenbacklog;
    
  + /*
  +  * The max child slot ever assigned, preserved across restarts.  Necessary
  +  * to deal with MaxClients changes across SIGUSR1 restarts.  We use this
  +  * value to optimize routines that have to scan the entire scoreboard.
  +  */
  + static int max_daemons_limit = -1;
  + 
  + /*
  +  * 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
  +  * sockets later on in the list.  The solution is to start at the listener
  +  * after the last one processed.  But to do that fast/easily in child_main it's
  +  * way more convenient for listeners to be a ring that loops back on itself.
  +  * The routine setup_listeners() is called after config time to both open up
  +  * the sockets and to turn the NULL-terminated list into a ring that loops back
  +  * on itself.
  +  *
  +  * head_listener is used by each child to keep track of what they consider
  +  * to be the "start" of the ring.  It is also set by make_child to ensure
  +  * that new children also don't starve any sockets.
  +  *
  +  * Note that listeners != NULL is ensured by read_config().
  +  */
  + listen_rec *listeners;
  + static listen_rec *head_listener;
  + 
    char server_root[MAX_STRING_LEN];
    char server_confname[MAX_STRING_LEN];
    
  ***************
  *** 919,932 ****
    /* XXX: things are seriously screwed if we ever have to do a partial
     * read or write ... we could get a corrupted scoreboard
     */
  ! static int force_write (int fd, char *buffer, int bufsz)
    {
        int rv, orig_sz = bufsz;
        
        do {
    	rv = write (fd, buffer, bufsz);
    	if (rv > 0) {
  ! 	    buffer += rv;
    	    bufsz -= rv;
    	}
        } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
  --- 945,958 ----
    /* XXX: things are seriously screwed if we ever have to do a partial
     * read or write ... we could get a corrupted scoreboard
     */
  ! static int force_write (int fd, void *buffer, int bufsz)
    {
        int rv, orig_sz = bufsz;
        
        do {
    	rv = write (fd, buffer, bufsz);
    	if (rv > 0) {
  ! 	    buffer = (char *)buffer + rv;
    	    bufsz -= rv;
    	}
        } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
  ***************
  *** 934,947 ****
        return rv < 0? rv : orig_sz - bufsz;
    }
    
  ! static int force_read (int fd, char *buffer, int bufsz)
    {
        int rv, orig_sz = bufsz;
        
        do {
    	rv = read (fd, buffer, bufsz);
    	if (rv > 0) {
  ! 	    buffer += rv;
    	    bufsz -= rv;
    	}
        } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
  --- 960,973 ----
        return rv < 0? rv : orig_sz - bufsz;
    }
    
  ! static int force_read (int fd, void *buffer, int bufsz)
    {
        int rv, orig_sz = bufsz;
        
        do {
    	rv = read (fd, buffer, bufsz);
    	if (rv > 0) {
  ! 	    buffer = (char *)buffer + rv;
    	    bufsz -= rv;
    	}
        } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
  ***************
  *** 977,984 ****
    
        memset ((char*)scoreboard_image, 0, sizeof(*scoreboard_image));
        scoreboard_image->global.exit_generation=exit_gen;
  !     force_write (scoreboard_fd, (char*)scoreboard_image,
  ! 		 sizeof(*scoreboard_image));
    #endif
    }
    
  --- 1003,1009 ----
    
        memset ((char*)scoreboard_image, 0, sizeof(*scoreboard_image));
        scoreboard_image->global.exit_generation=exit_gen;
  !     force_write (scoreboard_fd, scoreboard_image, sizeof(*scoreboard_image));
    #endif
    }
    
  ***************
  *** 1036,1043 ****
    {
    #ifdef SCOREBOARD_FILE
        lseek (scoreboard_fd, 0L, 0);
  !     force_read (scoreboard_fd, (char*)scoreboard_image,
  ! 		sizeof(*scoreboard_image));
    #endif
    }
    
  --- 1061,1067 ----
    {
    #ifdef SCOREBOARD_FILE
        lseek (scoreboard_fd, 0L, 0);
  !     force_read (scoreboard_fd, scoreboard_image, sizeof(*scoreboard_image));
    #endif
    }
    
  ***************
  *** 1048,1053 ****
  --- 1072,1089 ----
        return (scoreboard_image ? 1 : 0);
    }
    
  + static inline void put_scoreboard_info(int child_num,
  +     short_score *new_score_rec)
  + { 
  + #ifndef SCOREBOARD_FILE
  +     memcpy(&scoreboard_image->servers[child_num], new_score_rec,
  + 	   sizeof(short_score));
  + #else 
  +     lseek(scoreboard_fd, (long)child_num * sizeof(short_score), 0);
  +     force_write(scoreboard_fd, new_score_rec, sizeof(short_score));
  + #endif
  + }
  + 
    int update_child_status (int child_num, int status, request_rec *r)
    {
        int old_status;
  ***************
  *** 1092,1151 ****
        }
    #endif
    
  ! #ifndef SCOREBOARD_FILE
  !     memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof new_score_rec);
  ! #else
  !     lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0);
  !     force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score));
  ! #endif
    
        return old_status;
    }
    
  ! void update_scoreboard_global()
  !     {
    #ifdef SCOREBOARD_FILE
        lseek(scoreboard_fd,
    	  (char *)&scoreboard_image->global-(char *)scoreboard_image,0);
  !     force_write(scoreboard_fd,(char *)&scoreboard_image->global,
    		sizeof scoreboard_image->global);
    #endif
  -     }
  - 
  - int get_child_status (int child_num)
  - {
  -     if (child_num<0 || child_num>=HARD_SERVER_LIMIT)
  -     	return -1;
  -     else
  - 	return scoreboard_image->servers[child_num].status;
  - }
  - 
  - int count_busy_servers ()
  - {
  -     int i;
  -     int res = 0;
  - 
  -     for (i = 0; i < HARD_SERVER_LIMIT; ++i)
  -       if (scoreboard_image->servers[i].status == SERVER_BUSY_READ ||
  -               scoreboard_image->servers[i].status == SERVER_BUSY_WRITE ||
  -               scoreboard_image->servers[i].status == SERVER_BUSY_KEEPALIVE ||
  -               scoreboard_image->servers[i].status == SERVER_BUSY_LOG ||
  -               scoreboard_image->servers[i].status == SERVER_BUSY_DNS)
  -           ++res;
  -     return res;
    }
    
  - int count_live_servers()
  -     {
  -     int i;
  -     int res = 0;
  - 
  -     for (i = 0; i < HARD_SERVER_LIMIT; ++i)
  -       if (scoreboard_image->servers[i].status != SERVER_DEAD)
  - 	  ++res;
  -     return res;
  -     }
  - 
    short_score get_scoreboard_info(int i)
    {
        return (scoreboard_image->servers[i]);
  --- 1128,1148 ----
        }
    #endif
    
  !     put_scoreboard_info(child_num, &new_score_rec);
    
        return old_status;
    }
    
  ! static void update_scoreboard_global()
  ! {
    #ifdef SCOREBOARD_FILE
        lseek(scoreboard_fd,
    	  (char *)&scoreboard_image->global-(char *)scoreboard_image,0);
  !     force_write(scoreboard_fd,&scoreboard_image->global,
    		sizeof scoreboard_image->global);
    #endif
    }
    
    short_score get_scoreboard_info(int i)
    {
        return (scoreboard_image->servers[i]);
  ***************
  *** 1171,1228 ****
    
        times(&new_score_rec.times);
    
  ! 
  ! #ifndef SCOREBOARD_FILE
  !     memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof(short_score));
  ! #else
  !     lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0);
  !     force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score));
  ! #endif
    }
    #endif
    
  - int count_idle_servers ()
  - {
  -     int i;
  -     int res = 0;
  - 
  -     for (i = 0; i < HARD_SERVER_LIMIT; ++i)
  - 	if (scoreboard_image->servers[i].status == SERVER_READY)
  - 	    ++res;
  - 
  -     return res;
  - }
  - 
  - int find_free_child_num ()
  - {
  -     int i;
  - 
  -     for (i = 0; i < HARD_SERVER_LIMIT; ++i)
  - 	if (scoreboard_image->servers[i].status == SERVER_DEAD)
  - 	    return i;
  - 
  -     return -1;
  - }
    
  ! int find_child_by_pid (int pid)
    {
        int i;
    
  !     for (i = 0; i < HARD_SERVER_LIMIT; ++i)
    	if (scoreboard_image->servers[i].pid == pid)
    	    return i;
    
        return -1;
    }
    
  ! void reclaim_child_processes ()
    {
    #ifndef MULTITHREAD
        int i, status;
        int my_pid = getpid();
    
        sync_scoreboard_image();
  !     for (i = 0; i < HARD_SERVER_LIMIT; ++i) {
    	int pid = scoreboard_image->servers[i].pid;
    
    	if (pid != my_pid && pid != 0) { 
  --- 1168,1197 ----
    
        times(&new_score_rec.times);
    
  !     put_scoreboard_info(child_num, &new_score_rec); 
    }
    #endif
    
    
  ! static int find_child_by_pid (int pid)
    {
        int i;
    
  !     for (i = 0; i < max_daemons_limit; ++i)
    	if (scoreboard_image->servers[i].pid == pid)
    	    return i;
    
        return -1;
    }
    
  ! static void reclaim_child_processes ()
    {
    #ifndef MULTITHREAD
        int i, status;
        int my_pid = getpid();
    
        sync_scoreboard_image();
  !     for (i = 0; i < max_daemons_limit; ++i) {
    	int pid = scoreboard_image->servers[i].pid;
    
    	if (pid != my_pid && pid != 0) { 
  ***************
  *** 1292,1298 ****
        int status, n;
        int ret = 0;
    
  !     for (n = 0; n < HARD_SERVER_LIMIT; ++n) {
    	if (scoreboard_image->servers[n].status != SERVER_DEAD
    		&& waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG)
    		    == -1
  --- 1261,1267 ----
        int status, n;
        int ret = 0;
    
  !     for (n = 0; n < max_daemons_limit; ++n) {
    	if (scoreboard_image->servers[n].status != SERVER_DEAD
    		&& waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG)
    		    == -1
  ***************
  *** 1346,1351 ****
  --- 1315,1321 ----
        return(-1);
    
    #else /* WIN32 */
  +     struct timeval tv;
    #ifndef NEED_WAITPID
        int ret;
    
  ***************
  *** 1353,1369 ****
        if (ret == -1 && errno == EINTR) {
    	return -1;
        }
  !     if (ret <= 0) {
  ! 	sleep (1);
  ! 	return -1;
        }
  -     return ret;
    #else
  !     if (!reap_children ()) {
  ! 	sleep(1);
        }
  -     return -1;
    #endif
    #endif /* WIN32 */
    }
    
  --- 1323,1340 ----
        if (ret == -1 && errno == EINTR) {
    	return -1;
        }
  !     if (ret > 0) {
  ! 	return ret;
        }
    #else
  !     if (reap_children ()) {
  ! 	return -1;
        }
    #endif
  +     tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
  +     tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
  +     ap_select(0, NULL, NULL, NULL, &tv);
  +     return -1;
    #endif /* WIN32 */
    }
    
  ***************
  *** 1458,1471 ****
        if (sigaction (SIGTERM, &sa, NULL) < 0)
    	log_unixerr ("sigaction(SIGTERM)", NULL, NULL, server_conf);
    
  !     /* wait_or_timeout uses sleep() which could deliver a SIGALRM just as we're
  !      * trying to process the restart requests.  That's not good.  So we avoid
  !      * the race condition between when the restart request is made and when the
  !      * handler is invoked.
  !      *
  !      * We also want to ignore HUPs and USR1 while we're busy processing one.
  !      */
  !     sigaddset (&sa.sa_mask, SIGALRM);
        sigaddset (&sa.sa_mask, SIGHUP);
        sigaddset (&sa.sa_mask, SIGUSR1);
        sa.sa_handler = (void (*)())restart;
  --- 1429,1435 ----
        if (sigaction (SIGTERM, &sa, NULL) < 0)
    	log_unixerr ("sigaction(SIGTERM)", NULL, NULL, server_conf);
    
  !     /* we want to ignore HUPs and USR1 while we're busy processing one */
        sigaddset (&sa.sa_mask, SIGHUP);
        sigaddset (&sa.sa_mask, SIGUSR1);
        sa.sa_handler = (void (*)())restart;
  ***************
  *** 1881,1894 ****
        return s;
    }
    
    static listen_rec *old_listeners;
    
    static void copy_listeners(pool *p)
    {
        listen_rec *lr;
    
        ap_assert(old_listeners == NULL);
  !     for (lr = listeners; lr; lr = lr->next) {
    	listen_rec *nr = malloc(sizeof *nr);
    	if (nr == NULL) {
    	    fprintf (stderr, "Ouch!  malloc failed in copy_listeners()\n");
  --- 1845,1874 ----
        return s;
    }
    
  + 
  + /*
  +  * During a restart we keep track of the old listeners here, so that we
  +  * can re-use the sockets.  We have to do this because we won't be able
  +  * to re-open the sockets ("Address already in use").
  +  *
  +  * Unlike the listeners ring, old_listeners is a NULL terminated list.
  +  *
  +  * copy_listeners() makes the copy, find_listener() finds an old listener
  +  * and close_unused_listener() cleans up whatever wasn't used.
  +  */
    static listen_rec *old_listeners;
    
  + /* unfortunately copy_listeners may be called before listeners is a ring */
    static void copy_listeners(pool *p)
    {
        listen_rec *lr;
    
        ap_assert(old_listeners == NULL);
  !     if (listeners == NULL) {
  ! 	return;
  !     }
  !     lr = listeners;
  !     do {
    	listen_rec *nr = malloc(sizeof *nr);
    	if (nr == NULL) {
    	    fprintf (stderr, "Ouch!  malloc failed in copy_listeners()\n");
  ***************
  *** 1899,1905 ****
    	nr->next = old_listeners;
    	ap_assert(!nr->used);
    	old_listeners = nr;
  !     }
    }
    
    
  --- 1879,1886 ----
    	nr->next = old_listeners;
    	ap_assert(!nr->used);
    	old_listeners = nr;
  ! 	lr = lr->next;
  !     } while (lr && lr != listeners);
    }
    
    
  ***************
  *** 1931,1936 ****
  --- 1912,1964 ----
    }
    
    
  + /* open sockets, and turn the listeners list into a singly linked ring */
  + static void setup_listeners(pool *pconf)
  + {
  +     listen_rec *lr;
  +     int fd;
  + 
  +     listenmaxfd = -1;
  +     FD_ZERO (&listenfds);
  +     lr = listeners;
  +     for(;;) {
  + 	fd = find_listener (lr);
  + 	if (fd < 0) {
  + 	    fd = make_sock (pconf, &lr->local_addr);
  + 	}
  + 	FD_SET (fd, &listenfds);
  + 	if (fd > listenmaxfd) listenmaxfd = fd;
  + 	lr->fd = fd;
  + 	if (lr->next == NULL) break;
  + 	lr = lr->next;
  +     }
  +     /* turn the list into a ring */
  +     lr->next = listeners;
  +     head_listener = listeners;
  +     close_unused_listeners ();
  + }
  + 
  + 
  + /*
  +  * Find a listener which is ready for accept().  This advances the
  +  * head_listener global.
  +  */
  + static inline listen_rec *find_ready_listener(fd_set *main_fds)
  + {
  +     listen_rec *lr;
  +     
  +     lr = head_listener;
  +     do {
  + 	if (FD_ISSET(lr->fd, main_fds)) {
  + 	    head_listener = lr->next;
  + 	    return (lr);
  + 	}
  + 	lr = lr->next;
  +     } while (lr != head_listener);
  +     return NULL;
  + }
  + 
  + 
    static int s_iInitCount = 0;
    
    int
  ***************
  *** 2072,2079 ****
    	if (scoreboard_image->global.exit_generation >= generation)
    	    exit(0);
    	
  ! 	if ((count_idle_servers() >= daemons_max_free)
  ! 	    || (max_requests_per_child > 0
    	        && ++requests_this_child >= max_requests_per_child))
    	{
    	    exit(0);
  --- 2100,2106 ----
    	if (scoreboard_image->global.exit_generation >= generation)
    	    exit(0);
    	
  ! 	if ((max_requests_per_child > 0
    	        && ++requests_this_child >= max_requests_per_child))
    	{
    	    exit(0);
  ***************
  *** 2103,2111 ****
                if (srv <= 0)
                    continue;
    
  ! 	    for (lr = listeners; lr; lr = lr->next) {
  ! 		if (FD_ISSET(lr->fd, &main_fds)) break;
  !             }
    	    if (lr == NULL) continue;
    	    sd = lr->fd;
    
  --- 2130,2136 ----
                if (srv <= 0)
                    continue;
    
  ! 	    lr = find_ready_listener(&main_fds);
    	    if (lr == NULL) continue;
    	    sd = lr->fd;
    
  ***************
  *** 2269,2284 ****
        }    
    }
    
  ! int make_child(server_rec *server_conf, int child_num)
    {
        int pid;
    
        if (one_process) {
    	signal (SIGHUP, (void (*)())just_die);
    	signal (SIGTERM, (void (*)())just_die);
    	child_main (child_num);
        }
    
        Explain1 ("Starting new child in slot %d", child_num);
        (void)update_child_status (child_num, SERVER_STARTING, (request_rec *)NULL);
    
  --- 2294,2316 ----
        }    
    }
    
  ! static int make_child(server_rec *server_conf, int child_num)
    {
        int pid;
    
  +     if (child_num + 1 > max_daemons_limit) {
  + 	max_daemons_limit = child_num + 1;
  +     }
  + 
        if (one_process) {
    	signal (SIGHUP, (void (*)())just_die);
    	signal (SIGTERM, (void (*)())just_die);
    	child_main (child_num);
        }
    
  +     /* avoid starvation */
  +     head_listener = head_listener->next;
  + 
        Explain1 ("Starting new child in slot %d", child_num);
        (void)update_child_status (child_num, SERVER_STARTING, (request_rec *)NULL);
    
  ***************
  *** 2322,2327 ****
  --- 2354,2432 ----
    }
    
    
  + /* start up a bunch of children */
  + static void startup_children (int number_to_start)
  + {
  +     int i;
  + 
  +     for (i = 0; number_to_start && i < daemons_limit; ++i ) {
  + 	if (scoreboard_image->servers[i].status != SERVER_DEAD) {
  + 	    continue;
  + 	}
  + 	if (make_child (server_conf, i) < 0) {
  + 	    break;
  + 	}
  + 	--number_to_start;
  +     }
  + }
  + 
  + 
  + static void perform_idle_server_maintenance ()
  + {
  +     int i;
  +     int to_kill;
  +     int free_slot;
  +     int idle_count;
  + 
  +     free_slot = -1;
  +     to_kill = -1;
  +     idle_count = 0;
  +     sync_scoreboard_image ();
  +     for (i = 0; i < daemons_limit; ++i) {
  + 	switch (scoreboard_image->servers[i].status) {
  + 	case 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).
  + 	     */
  + 	    to_kill = i;
  + 	    break;
  + 	case SERVER_DEAD:
  + 	    /* try to keep children numbers as low as possible */
  + 	    if (free_slot == -1) {
  + 		free_slot = i;
  + 	    }
  + 	    break;
  + 	}
  +     }
  +     if (idle_count > daemons_max_free) {
  + 	/* 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 (SIGUSR1, scoreboard_image->servers[to_kill].pid);
  +     } else if (idle_count < daemons_min_free) {
  + 	if (free_slot == -1) {
  + 	    /* only report this condition once */
  + 	    static int reported = 0;
  + 
  + 	    if (!reported) {
  + 		log_printf (server_conf,
  + 		    "server reached MaxClients setting, consider"
  + 		    " raising the MaxClients setting");
  + 		reported = 1;
  + 	    }
  + 	} else {
  + 	    make_child (server_conf, free_slot);
  + 	}
  +     }
  + }
  + 
  + 
  + 
    /*****************************************************************
     * Executive routines.
     */
  ***************
  *** 2332,2341 ****
    void standalone_main(int argc, char **argv)
    {
        int remaining_children_to_start;
  -     listen_rec *lr;
    
        standalone = 1;
  -     listenmaxfd = -1;
    
        is_graceful = 0;
        ++generation;
  --- 2437,2444 ----
  ***************
  *** 2356,2377 ****
    	ptrans = make_sub_pool (pconf);
    
    	server_conf = read_config (pconf, ptrans, server_confname); 
  ! 
  ! 	listenmaxfd = -1;
  ! 	FD_ZERO (&listenfds);
  ! 	for (lr = listeners; lr != NULL; lr = lr->next) {
  ! 	    int fd;
  ! 	    
  ! 	    fd = find_listener (lr);
  ! 	    if (fd < 0) {
  ! 		fd = make_sock (pconf, &lr->local_addr);
  ! 	    }
  ! 	    FD_SET (fd, &listenfds);
  ! 	    if (fd > listenmaxfd) listenmaxfd = fd;
  ! 	    lr->fd = fd;
  ! 	}
  ! 	close_unused_listeners ();
  ! 
    	init_modules (pconf, server_conf);
    	open_logs (server_conf, pconf);
    	set_group_privs ();
  --- 2459,2465 ----
    	ptrans = make_sub_pool (pconf);
    
    	server_conf = read_config (pconf, ptrans, server_confname); 
  ! 	setup_listeners (pconf);
    	init_modules (pconf, server_conf);
    	open_logs (server_conf, pconf);
    	set_group_privs ();
  ***************
  *** 2385,2391 ****
    	    note_cleanups_for_fd (pconf, scoreboard_fd);
    	}
    #endif
  - 
    	default_server_hostnames (server_conf);
    
    	set_signals ();
  --- 2473,2478 ----
  ***************
  *** 2407,2416 ****
    	    remaining_children_to_start = daemons_limit;
    	}
    	if (!is_graceful) {
  ! 	    while (remaining_children_to_start) {
  ! 		--remaining_children_to_start;
  ! 		make_child (server_conf, remaining_children_to_start);
  ! 	    }
    	}
    
    	log_error ("Server configured -- resuming normal operations",
  --- 2494,2501 ----
    	    remaining_children_to_start = daemons_limit;
    	}
    	if (!is_graceful) {
  ! 	    startup_children (remaining_children_to_start);
  ! 	    remaining_children_to_start = 0;
    	}
    
    	log_error ("Server configured -- resuming normal operations",
  ***************
  *** 2433,2438 ****
  --- 2518,2533 ----
    		if (child_slot >= 0) {
    		    (void)update_child_status (child_slot, SERVER_DEAD,
    			(request_rec *)NULL);
  + 		    if (remaining_children_to_start
  + 			&& child_slot < daemons_limit) {
  + 			/* we're still doing a 1-for-1 replacement of dead
  + 			 * children with new children
  + 			 */
  + 			make_child (server_conf, child_slot);
  + 			--remaining_children_to_start;
  + 			/* don't perform idle maintenance yet */
  + 			continue;
  + 		    }
    		} else if (is_graceful) {
    		    /* Great, we've probably just lost a slot in the
    		     * scoreboard.  Somehow we don't know about this
  ***************
  *** 2446,2463 ****
    	 	 * generation of children needed to be reaped... so assume
    		 * they're all done, and pick up the slack if any is left.
    		 */
  ! 		while (remaining_children_to_start > 0) {
  ! 		    child_slot = find_free_child_num ();
  ! 		    if (child_slot < 0 || child_slot >= daemons_limit) {
  ! 			remaining_children_to_start = 0;
  ! 			break;
  ! 		    }
  ! 		    if (make_child (server_conf, child_slot) < 0) {
  ! 			remaining_children_to_start = 0;
  ! 			break;
  ! 		    }
  ! 		    --remaining_children_to_start;
  ! 		}
    		/* In any event we really shouldn't do the code below because
    		 * few of the servers we just started are in the IDLE state
    		 * yet, so we'd mistakenly create an extra server.
  --- 2541,2548 ----
    	 	 * generation of children needed to be reaped... so assume
    		 * they're all done, and pick up the slack if any is left.
    		 */
  ! 		startup_children (remaining_children_to_start);
  ! 		remaining_children_to_start = 0;
    		/* In any event we really shouldn't do the code below because
    		 * few of the servers we just started are in the IDLE state
    		 * yet, so we'd mistakenly create an extra server.
  ***************
  *** 2465,2480 ****
    		continue;
    	    }
    
  ! 	    sync_scoreboard_image ();
  ! 	    if ((remaining_children_to_start
  ! 		    || (count_idle_servers () < daemons_min_free))
  ! 		&& (child_slot = find_free_child_num ()) >= 0
  ! 		&& child_slot < daemons_limit) {
  ! 		make_child (server_conf, child_slot);
  ! 	    }
  ! 	    if (remaining_children_to_start) {
  ! 		--remaining_children_to_start;
  ! 	    }
    	}
    
    	/* we've been told to restart */
  --- 2550,2556 ----
    		continue;
    	    }
    
  ! 	    perform_idle_server_maintenance();
    	}
    
    	/* we've been told to restart */
  ***************
  *** 3018,3024 ****
        int max_jobs_after_exit_request;
    
        standalone = 1;
  !     sd = listenmaxfd = -1;
        nthreads = threads_per_child;
        max_jobs_after_exit_request = excess_requests_per_child;
        max_jobs_per_exe = max_requests_per_child;
  --- 3094,3100 ----
        int max_jobs_after_exit_request;
    
        standalone = 1;
  !     sd = -1;
        nthreads = threads_per_child;
        max_jobs_after_exit_request = excess_requests_per_child;
        max_jobs_per_exe = max_requests_per_child;
  ***************
  *** 3040,3067 ****
        default_server_hostnames (server_conf);
    
        acquire_mutex(start_mutex);
  -     {
  - 	listen_rec *lr;
  - 	int fd;
  - 
  -         listenmaxfd = -1;
  - 	FD_ZERO(&listenfds);
  - 
  - 	for (lr=listeners; lr != NULL; lr=lr->next)
  - 	{
  - 	    fd=find_listener(lr);
  - 	    if(fd < 0)
  - 	    {
  - 		fd = make_sock(pconf, &lr->local_addr);
  - 	    }
  - 	    FD_SET(fd, &listenfds);
  - 	    if (fd > listenmaxfd) listenmaxfd = fd;
  - 	    lr->fd=fd;
  - 	}
  - 	close_unused_listeners();
  - 	sd = -1;
  -     }
    
        set_signals();
    
        /*
  --- 3116,3123 ----
        default_server_hostnames (server_conf);
    
        acquire_mutex(start_mutex);
    
  +     setup_listeners(pconf);
        set_signals();
    
        /*
  ***************
  *** 3090,3095 ****
  --- 3146,3154 ----
            {
                child_handles[i] = create_thread((void (*)(void *))child_main, (void *)i);
            }
  + 	if (nthreads > max_daemons_limit) {
  + 	    max_daemons_limit = nthreads;
  + 	}
        }
    
        /* main loop */
  ***************
  *** 3104,3120 ****
                start_mutex_released = 1;
                /* set the listen queue to 1 */
                {
  ! 		listen_rec *lr;
    		
  ! 		for (lr=listeners; lr != NULL; lr=lr->next)
  ! 		{
    		/* to prove a point - Ben */
    		    ap_assert(!lr->used);
    		    if(lr->used)
    		    {
    			listen(lr->fd, 1);
    		    }
  ! 		}
                }
            }
            if(!start_exit)
  --- 3163,3179 ----
                start_mutex_released = 1;
                /* set the listen queue to 1 */
                {
  ! 		listen_rec *lr = listeners;
    		
  ! 		do {
    		/* to prove a point - Ben */
    		    ap_assert(!lr->used);
    		    if(lr->used)
    		    {
    			listen(lr->fd, 1);
    		    }
  ! 		    lr = lr->next;
  ! 		} while (lr != listeners);
                }
            }
            if(!start_exit)
  ***************
  *** 3160,3180 ****
    
            {
    	    listen_rec *lr;
  ! 	    int fd;
  ! 	    
  ! 	    for (lr=listeners; lr != NULL; lr=lr->next)
  ! 	    {
  ! /*	        if(!lr->used)
  !                     continue;*/
  !                 fd=lr->fd;
  ! 	        
  ! 	        if(FD_ISSET(fd, &listenfds))
  ! 		    {
  ! 		    sd = fd;
  ! 		    break;
  ! 		    }
    	    }
  !         }
    
            do {
                clen = sizeof(sa_client);
  --- 3219,3230 ----
    
            {
    	    listen_rec *lr;
  ! 
  ! 	    lr = find_ready_listener (&listenfds);
  ! 	    if (lr != NULL) {
  ! 		sd = lr->fd;
    	    }
  ! 	}
    
            do {
                clen = sizeof(sa_client);
  ***************
  *** 3216,3223 ****
        {
    	listen_rec *lr;
    	
  ! 	for (lr=listeners; lr != NULL; lr=lr->next)
  ! 	{
    	/* prove the point again */
    	    ap_assert(!lr->used);
    	    if(lr->used)
  --- 3266,3273 ----
        {
    	listen_rec *lr;
    	
  ! 	lr = listeners;
  ! 	do {
    	/* prove the point again */
    	    ap_assert(!lr->used);
    	    if(lr->used)
  ***************
  *** 3225,3231 ****
    		closesocket(lr->fd);
    		lr->fd = -1;
    	    }
  ! 	}
        }
    
        for(i=0; i<nthreads; i++)
  --- 3275,3282 ----
    		closesocket(lr->fd);
    		lr->fd = -1;
    	    }
  ! 	    lr = lr->next;
  ! 	} while (lr != listeners);
        }
    
        for(i=0; i<nthreads; i++)
  
  
  
  1.12      +0 -4      apache/src/http_main.h
  
  Index: http_main.h
  ===================================================================
  RCS file: /export/home/cvs/apache/src/http_main.h,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -C3 -r1.11 -r1.12
  *** http_main.h	1997/06/15 19:22:26	1.11
  --- http_main.h	1997/06/30 21:10:01	1.12
  ***************
  *** 93,101 ****
    
    void sync_scoreboard_image ();
    int update_child_status (int child_num, int status, request_rec *r);
  - int get_child_status (int child_num);
  - int count_busy_servers ();
  - int count_idle_servers ();
  - 
    unsigned int set_callback_and_alarm(void (*fn)(int), int x);
    int check_alarm();
  --- 93,97 ----
  
  
  
  1.120     +15 -0     apache/src/httpd.h
  
  Index: httpd.h
  ===================================================================
  RCS file: /export/home/cvs/apache/src/httpd.h,v
  retrieving revision 1.119
  retrieving revision 1.120
  diff -C3 -r1.119 -r1.120
  *** httpd.h	1997/06/29 19:19:36	1.119
  --- httpd.h	1997/06/30 21:10:02	1.120
  ***************
  *** 231,236 ****
  --- 231,251 ----
    #define HARD_SERVER_LIMIT 256
    #endif
    
  + /*
  +  * (Unix, OS/2 only)
  +  * Interval, in microseconds, between scoreboard maintenance.  During
  +  * each scoreboard maintenance cycle the parent decides if it needs to
  +  * spawn a new child (to meet MinSpareServers requirements), or kill off
  +  * a child (to meet MaxSpareServers requirements).  It will only spawn or
  +  * kill one child per cycle.  Setting this too low will chew cpu.  The
  +  * default is probably sufficient for everyone.  But some people may want
  +  * to raise this on servers which aren't dedicated to httpd and where they
  +  * don't like the httpd waking up each second to see what's going on.
  +  */
  + #ifndef SCOREBOARD_MAINTENANCE_INTERVAL
  + #define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
  + #endif
  + 
    /* Number of requests to try to handle in a single process.  If <= 0,
     * the children don't die off.  That's the default here, since I'm still
     * interested in finding and stanching leaks.