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.