manoj 99/07/23 15:15:18
Modified: mpm/src/modules/mpm/dexter dexter.c dexter.h Log: Add support for dynamically creating and destroying threads based on a Min/MaxSpareThreads algorithm, and clean up a couple of stray pieces of gunk from the days of scoreboardhood. Revision Changes Path 1.4 +138 -65 apache-2.0/mpm/src/modules/mpm/dexter/dexter.c Index: dexter.c =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/modules/mpm/dexter/dexter.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -u -r1.3 -r1.4 --- dexter.c 1999/07/21 22:18:30 1.3 +++ dexter.c 1999/07/23 22:15:10 1.4 @@ -78,12 +78,12 @@ * Actual definitions of config globals */ -int ap_threads_per_child=0; /* Worker threads per child */ -int ap_max_requests_per_child=0; +static int threads_to_start=0; /* Worker threads per child */ +static int min_spare_threads=0; +static int max_spare_threads=HARD_THREAD_LIMIT; +static int max_requests_per_child=0; static char *ap_pid_fname=NULL; -static int ap_num_daemons=0; -static time_t ap_restart_time=0; -API_VAR_EXPORT int ap_extended_status = 0; +static int num_daemons=0; static int workers_may_exit = 0; static int requests_this_child; static int num_listenfds = 0; @@ -145,12 +145,22 @@ static pool *pconf; /* Pool for config stuff */ static pool *pchild; /* Pool for httpd child stuff */ +typedef struct { + pool *pool; + pthread_mutex_t mutex; + pthread_attr_t attr; +} worker_thread_info; + static int my_pid; /* Linux getpid() doesn't work except in main thread. Use this instead */ /* Keep track of the number of worker threads currently active */ static int worker_thread_count; static pthread_mutex_t worker_thread_count_mutex; +/* Keep track of the number of idle worker threads */ +static int idle_thread_count; +static pthread_mutex_t idle_thread_count_mutex; + /* Global, alas, so http_core can talk to us */ enum server_token_type ap_server_tokens = SrvTk_FULL; @@ -830,11 +840,30 @@ ap_process_connection(current_conn); } -static void * worker_thread(void *thread_pool) +static void *worker_thread(void *); + +static void start_thread(worker_thread_info *thread_info) { - pool *tpool = thread_pool; + pthread_t thread; + + if (pthread_create(&thread, &(thread_info->attr), worker_thread, thread_info)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "pthread_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); + workers_may_exit = 1; + } +} + +/* idle_thread_count should be incremented before starting a worker_thread */ + +static void *worker_thread(void *arg) +{ struct sockaddr sa_client; int csd = -1; + pool *tpool; /* Pool for this thread */ pool *ptrans; /* Pool for per-transaction stuff */ int sd = -1; int srv; @@ -842,7 +871,12 @@ char pipe_read_char; int curr_pollfd, last_pollfd = 0; size_t len = sizeof(struct sockaddr); + worker_thread_info *thread_info = arg; + int thread_just_started = 1; + pthread_mutex_lock(&thread_info->mutex); + tpool = ap_make_sub_pool(thread_info->pool); + pthread_mutex_unlock(&thread_info->mutex); ptrans = ap_make_sub_pool(tpool); pthread_mutex_lock(&worker_thread_count_mutex); @@ -852,9 +886,22 @@ /* TODO: Switch to a system where threads reuse the results from earlier poll calls - manoj */ while (!workers_may_exit) { - workers_may_exit |= (ap_max_requests_per_child != 0) && (requests_this_child <= 0); + workers_may_exit |= (max_requests_per_child != 0) && (requests_this_child <= 0); if (workers_may_exit) break; - + if (!thread_just_started) { + pthread_mutex_lock(&idle_thread_count_mutex); + if (idle_thread_count < max_spare_threads) { + idle_thread_count++; + pthread_mutex_unlock(&idle_thread_count_mutex); + } + else { + pthread_mutex_unlock(&idle_thread_count_mutex); + break; + } + } + else { + thread_just_started = 0; + } SAFE_ACCEPT(intra_mutex_on(0)); if (workers_may_exit) { SAFE_ACCEPT(intra_mutex_off(0)); @@ -863,6 +910,7 @@ SAFE_ACCEPT(accept_mutex_on(0)); while (!workers_may_exit) { srv = poll(listenfds, num_listenfds + 1, -1); + if (srv < 0) { if (errno == EINTR) { continue; @@ -874,7 +922,6 @@ ap_get_server_conf(), "poll: (listen)"); workers_may_exit = 1; } - if (workers_may_exit) break; if (listenfds[0].revents & POLLIN) { @@ -926,12 +973,22 @@ csd = ap_accept(sd, &sa_client, &len); SAFE_ACCEPT(accept_mutex_off(0)); SAFE_ACCEPT(intra_mutex_off(0)); + pthread_mutex_lock(&idle_thread_count_mutex); + if (idle_thread_count > min_spare_threads) { + idle_thread_count--; + } + else { + start_thread(thread_info); + } + pthread_mutex_unlock(&idle_thread_count_mutex); } else { SAFE_ACCEPT(accept_mutex_off(0)); SAFE_ACCEPT(intra_mutex_off(0)); + pthread_mutex_lock(&idle_thread_count_mutex); + idle_thread_count--; + pthread_mutex_unlock(&idle_thread_count_mutex); break; } - process_socket(ptrans, &sa_client, csd); ap_clear_pool(ptrans); requests_this_child--; @@ -954,10 +1011,8 @@ { sigset_t sig_mask; int signal_received; - pthread_t thread; - pthread_attr_t thread_attr; int i; - pool *tpool; + worker_thread_info thread_info; ap_listen_rec *lr; my_pid = getpid(); @@ -983,7 +1038,7 @@ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, "pthread_sigmask"); } - requests_this_child = ap_max_requests_per_child; + requests_this_child = max_requests_per_child; /* Set up the pollfd array */ listenfds = ap_palloc(pchild, sizeof(struct pollfd) * (num_listenfds + 1)); @@ -998,32 +1053,21 @@ /* Setup worker threads */ + idle_thread_count = threads_to_start; worker_thread_count = 0; + thread_info.pool = ap_make_sub_pool(pconf); + pthread_mutex_init(&thread_info.mutex, NULL); + pthread_mutex_init(&idle_thread_count_mutex, NULL); pthread_mutex_init(&worker_thread_count_mutex, NULL); pthread_mutex_init(&pipe_of_death_mutex, NULL); - pthread_attr_init(&thread_attr); - pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); - for (i=0; i < ap_threads_per_child; i++) { - tpool = ap_make_sub_pool(pchild); - - /* We are creating threads right now */ - if (pthread_create(&thread, &thread_attr, worker_thread, tpool)) { - ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, - "pthread_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); - } + pthread_attr_init(&thread_info.attr); + pthread_attr_setdetachstate(&thread_info.attr, PTHREAD_CREATE_DETACHED); - /* We let each thread update it's own scoreboard entry. This is done - * because it let's us deal with tid better. - */ + /* We are creating worker threads right now */ + for (i=0; i < threads_to_start; i++) { + start_thread(&thread_info); } - pthread_attr_destroy(&thread_attr); - /* This thread will be the one responsible for handling signals */ sigemptyset(&sig_mask); sigaddset(&sig_mask, SIGTERM); @@ -1099,7 +1143,7 @@ { int i; - for (i = 0; number_to_start && i < ap_num_daemons; ++i) { + for (i = 0; number_to_start && i < num_daemons; ++i) { if (ap_scoreboard_image[i].status != SERVER_DEAD) { continue; } @@ -1137,7 +1181,7 @@ ap_check_signals(); - for (i = 0; i < ap_num_daemons; ++i) { + for (i = 0; i < num_daemons; ++i) { unsigned char status = ap_scoreboard_image[i].status; if (status == SERVER_DEAD) { @@ -1187,7 +1231,7 @@ ap_update_child_status(child_slot, SERVER_DEAD); if (remaining_children_to_start - && child_slot < ap_num_daemons) { + && child_slot < num_daemons) { /* we're still doing a 1-for-1 replacement of dead * children with new children */ @@ -1279,7 +1323,7 @@ * start more than that, so we'll just keep track of how many we're * supposed to start up without the 1 second penalty between each fork. */ - remaining_children_to_start = ap_num_daemons; + remaining_children_to_start = num_daemons; if (!is_graceful) { remaining_children_to_start = \ startup_children(remaining_children_to_start); @@ -1344,13 +1388,13 @@ * gracefully dealing with existing request. */ - for (i = 0; i < ap_num_daemons; ++i) { + for (i = 0; i < num_daemons; ++i) { if (ap_scoreboard_image[i].status != SERVER_DEAD) { ap_scoreboard_image[i].status = SERVER_DYING; } } /* kill off the idle ones */ - for (i = 0; i < ap_num_daemons; ++i) { + for (i = 0; i < num_daemons; ++i) { if (write(pipe_of_death[1], &char_of_death, 1) == -1) { ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "write pipe_of_death"); } @@ -1368,9 +1412,6 @@ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, "SIGHUP received. Attempting to restart"); } - if (!is_graceful) { - ap_restart_time = time(NULL); /* ZZZZZ */ - } return 0; } @@ -1399,12 +1440,11 @@ unixd_pre_config(); ap_listen_pre_config(); - ap_num_daemons = HARD_SERVER_LIMIT; - ap_threads_per_child = DEFAULT_THREADS_PER_CHILD; + num_daemons = HARD_SERVER_LIMIT; + threads_to_start = DEFAULT_THREADS_PER_CHILD; ap_pid_fname = DEFAULT_PIDLOG; ap_lock_fname = DEFAULT_LOCKFILE; - ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; - ap_extended_status = 0; + max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); } @@ -1441,45 +1481,74 @@ return err; } - ap_num_daemons = atoi(arg); - if (ap_num_daemons > HARD_SERVER_LIMIT) { + num_daemons = atoi(arg); + if (num_daemons > HARD_SERVER_LIMIT) { fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " - "of %d servers,\n", ap_num_daemons, HARD_SERVER_LIMIT); + "of %d servers,\n", num_daemons, HARD_SERVER_LIMIT); fprintf(stderr, " lowering MaxClients to %d. To increase, please " "see the\n", HARD_SERVER_LIMIT); fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); - ap_num_daemons = HARD_SERVER_LIMIT; + num_daemons = HARD_SERVER_LIMIT; } - else if (ap_num_daemons < 1) { + else if (num_daemons < 1) { fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); - ap_num_daemons = 1; + num_daemons = 1; } return NULL; } -static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) +static const char *set_threads_to_start (cmd_parms *cmd, void *dummy, char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } - ap_threads_per_child = atoi(arg); - if (ap_threads_per_child > HARD_THREAD_LIMIT) { - fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time" - "limit of %d threads,\n", ap_threads_per_child, + threads_to_start = atoi(arg); + if (threads_to_start > HARD_THREAD_LIMIT) { + fprintf(stderr, "WARNING: StartThreads of %d exceeds compile time" + "limit of %d threads,\n", threads_to_start, HARD_THREAD_LIMIT); - fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please" + fprintf(stderr, " lowering StartThreads to %d. To increase, please" "see the\n", HARD_THREAD_LIMIT); fprintf(stderr, " HARD_THREAD_LIMIT define in src/include/httpd.h.\n"); } - else if (ap_threads_per_child < 1) { - fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); - ap_threads_per_child = 1; + else if (threads_to_start < 1) { + fprintf(stderr, "WARNING: Require StartThreads > 0, setting to 1\n"); + threads_to_start = 1; + } + return NULL; +} + +static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + min_spare_threads = atoi(arg); + if (min_spare_threads <= 0) { + fprintf(stderr, "WARNING: detected MinSpareThreads set to non-positive.\n"); + fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); + fprintf(stderr, "Please read the documentation.\n"); + min_spare_threads = 1; } + return NULL; } +static const char *set_max_free_threads(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + max_spare_threads = atoi(arg); + return NULL; +} + static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); @@ -1487,7 +1556,7 @@ return err; } - ap_max_requests_per_child = atoi(arg); + max_requests_per_child = atoi(arg); return NULL; } @@ -1553,8 +1622,12 @@ "The lockfile used when Apache needs to lock the accept() call"}, { "NumServers", set_num_daemons, NULL, RSRC_CONF, TAKE1, "Number of children alive at the same time" }, -{ "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1, +{ "StartThreads", set_threads_to_start, NULL, RSRC_CONF, TAKE1, "Number of threads each child creates" }, +{ "MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF, TAKE1, + "Minimum number of idle threads per child, to handle request spikes" }, +{ "MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF, TAKE1, + "Maximum number of idle threads per child" }, { "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, "Maximum number of requests a particular child serves before dying." }, { "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, 1.2 +0 -5 apache-2.0/mpm/src/modules/mpm/dexter/dexter.h Index: dexter.h =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/modules/mpm/dexter/dexter.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -u -r1.1 -r1.2 --- dexter.h 1999/07/21 19:07:06 1.1 +++ dexter.h 1999/07/23 22:15:15 1.2 @@ -58,12 +58,7 @@ #ifndef APACHE_MPM_MPMT_PTHREAD_H #define APACHE_MPM_MPMT_PTHREAD_H -extern int ap_threads_per_child; extern int max_daemons_limit; -extern int ap_max_requests_per_child; -extern int ap_pipe_of_death[2]; -extern void clean_child_exit(int); -extern int ap_extended_status; extern void clean_child_exit(int); #endif /* APACHE_MPM_MPMT_PTHREAD_H */