updated when sessions begin and end.
* Replaced fixed number of worker threads with lower and upper limits; current
code now tries to make thread count == session count, within these limits
$Log$
+Revision 1.416 1999/11/17 04:15:05 ajc
+* Removed the session_count() function. Instead, keep a reference count
+ updated when sessions begin and end.
+* Replaced fixed number of worker threads with lower and upper limits; current
+ code now tries to make thread count == session count, within these limits
+
Revision 1.415 1999/11/15 03:17:39 ajc
* Put lockfile in /tmp instead of in /var/lock. The latter is not guaranteed
to exist, nor is it guaranteed to be writable by BBSUID
Fri Jul 10 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
* Initial CVS import
+
char c_logpages[ROOMNAMELEN]; /* Room to log pages to (or not) */
char c_createax; /* Axlevel required to create rooms */
long c_maxmsglen; /* Maximum message length */
- int c_worker_threads; /* Number of worker threads to start*/
+ int c_min_workers; /* Lower limit on number of threads */
+ int c_max_workers; /* Upper limit on number of threads */
};
#define NODENAME config.c_nodename
}
/* Remove the context from the global context list. This needs
- * to get done FIRST to avoid concurrency problems.
+ * to get done FIRST to avoid concurrency problems. It is *vitally*
+ * important to keep num_sessions accurate!!
*/
lprintf(7, "Removing context for session %d\n", con->cs_pid);
begin_critical_section(S_SESSION_TABLE);
if (ContextList == con) {
ToFree = ContextList;
ContextList = ContextList->next;
+ --num_sessions;
}
else {
for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
if (ptr->next == con) {
ToFree = ptr->next;
ptr->next = ptr->next->next;
+ --num_sessions;
}
}
}
*/
void begin_session(struct CitContext *con)
{
- int num_sessions, len;
+ int len;
struct sockaddr_in sin;
/*
con->dl_is_net = 0;
con->FirstSessData = NULL;
- num_sessions = session_count();
con->nologin = 0;
if ((config.c_maxsessions > 0)&&(num_sessions > config.c_maxsessions))
con->nologin = 1;
if (config.c_maxmsglen < 8192)
config.c_maxmsglen = 8192;
- /* Default number of worker threads is 15 and the minimum is 5
- */
- /* Can't have fewer than two worker threads */
- if (config.c_worker_threads == 0)
- config.c_worker_threads = 15;
- if (config.c_worker_threads < 5)
- config.c_worker_threads = 5;
-
+ /* Default lower and upper limits on number of worker threads */
+
+ if (config.c_min_workers < 3) /* no less than 3 */
+ config.c_min_workers = 5;
+
+ if (config.c_max_workers == 0) /* default maximum */
+ config.c_max_workers = 256;
+
+ if (config.c_max_workers < config.c_min_workers) /* max >= min */
+ config.c_max_workers = config.c_min_workers;
}
cprintf("%s\n", config.c_logpages);
cprintf("%d\n", config.c_createax);
cprintf("%d\n", config.c_maxmsglen);
- cprintf("%d\n", config.c_worker_threads);
+ cprintf("%d\n", config.c_min_workers);
+ cprintf("%d\n", config.c_max_workers);
cprintf("000\n");
}
config.c_maxmsglen = atoi(buf);
break;
case 21: if (atoi(buf) >= 2)
- config.c_worker_threads = atoi(buf);
+ config.c_min_workers = atoi(buf);
+ case 22: if (atoi(buf) >= config.c_min_workers)
+ config.c_max_workers = atoi(buf);
}
++a;
}
void do_system_configuration(void)
{
char buf[256];
- char sc[22][256];
+ char sc[23][256];
int expire_mode = 0;
int expire_value = 0;
int a;
if (buf[0] == '1') {
a = 0;
while (serv_gets(buf), strcmp(buf, "000")) {
- if (a < 22)
+ if (a < 23)
strcpy(&sc[a][0], buf);
++a;
}
strprompt("Default room purge time (days)", &sc[17][0], 5);
strprompt("Name of room to log pages", &sc[18][0], ROOMNAMELEN);
strprompt("Maximum message length", &sc[20][0], 20);
- strprompt("Number of worker threads", &sc[21][0], 3);
+ strprompt("Minimum number of worker threads", &sc[21][0], 3);
+ strprompt("Maximum number of worker threads", &sc[22][0], 3);
/* Angels and demons dancing in my head... */
do {
serv_puts("CONF set");
serv_gets(buf);
if (buf[0] == '4') {
- for (a = 0; a < 22; ++a)
+ for (a = 0; a < 23; ++a)
serv_puts(&sc[a][0]);
serv_puts("000");
}
struct CitContext masterCC;
int rescan[2]; /* The Rescan Pipe */
time_t last_purge = 0; /* Last dead session purge */
+int num_threads = 0; /* Current number of threads */
+int num_sessions = 0; /* Current number of sessions */
/*
* lprintf() ... Write logging information
me->cs_pid = num;
me->next = ContextList;
ContextList = me;
+ ++num_sessions;
end_critical_section(S_SESSION_TABLE);
return(me);
-/*
- * Return the number of sessions currently running.
- * (This should probably be moved out of sysdep.c)
- */
-int session_count(void) {
- struct CitContext *ptr;
- int TheCount = 0;
-
- begin_critical_section(S_SESSION_TABLE);
- for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
- ++TheCount;
- }
- end_critical_section(S_SESSION_TABLE);
-
- return(TheCount);
-}
-
-
/*
* client_write() ... Send binary data to the client.
*/
* This function has code to prevent it from running more than once every
* few seconds, because running it after every single unbind would waste a lot
* of CPU time and keep the context list locked too much.
+ *
+ * After that's done, we raise or lower the size of the worker thread pool
+ * if such an action is appropriate.
*/
void dead_session_purge(void) {
struct CitContext *ptr, *rem;
+ pthread_attr_t attr;
+ pthread_t newthread;
if ( (time(NULL) - last_purge) < 5 ) return; /* Too soon, go away */
time(&last_purge);
begin_critical_section(S_SESSION_TABLE);
for (ptr = ContextList; ptr != NULL; ptr = ptr->next) {
if ( (ptr->state == CON_IDLE) && (ptr->kill_me) ) {
- rem = ptr;
+ rem = ptr;
}
}
end_critical_section(S_SESSION_TABLE);
}
} while (rem != NULL);
+
+
+ /* Raise or lower the size of the worker thread pool if such
+ * an action is appropriate.
+ */
+
+ if ( (num_sessions > num_threads)
+ && (num_threads < config.c_max_workers) ) {
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&newthread, &attr,
+ (void* (*)(void*)) worker_thread, NULL) != 0) {
+ lprintf(1, "Can't create worker thead: %s\n",
+ strerror(errno));
+ }
+
+ }
+
+ else if ( (num_sessions < num_threads)
+ && (num_threads > config.c_min_workers) ) {
+ --num_threads;
+ pthread_exit(NULL);
+ }
+
}
/*
* Now create a bunch of worker threads.
*/
- for (i=0; i<(config.c_worker_threads-1); ++i) {
+ for (i=0; i<(config.c_min_workers-1); ++i) {
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&HousekeepingThread, &attr,
int alen; /* Data for master socket */
int ssock; /* Descriptor for client socket */
+ ++num_threads;
while (!time_to_die) {
/*
- * In a stupid environment, we would have all idle threads
+ * A naive implementation would have all idle threads
* calling select() and then they'd all wake up at once. We
* solve this problem by putting the select() in a critical
* section, so only one thread has the opportunity to wake
/* If control reaches this point, the server is shutting down */
master_cleanup();
+ --num_threads;
pthread_exit(NULL);
}
struct CitContext *MyContext (void);
struct CitContext *CreateNewContext (void);
void InitMyContext (struct CitContext *con);
-int session_count (void);
void client_write (char *buf, int nbytes);
void cprintf (const char *format, ...);
int client_read_to (char *buf, int bytes, int timeout);
void cmd_nset (char *cmdbuf);
int convert_login (char *NameToConvert);
void worker_thread (void);
+
+extern int num_sessions;