You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ma...@hyperreal.org on 1999/07/24 00:15:20 UTC
cvs commit: apache-2.0/mpm/src/modules/mpm/dexter dexter.c dexter.h
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 */