diff --git a/security/nss/cmd/selfserv/selfserv.c b/security/nss/cmd/selfserv/selfserv.c index 496e9a7bd0c2..c6abe1de1f19 100644 --- a/security/nss/cmd/selfserv/selfserv.c +++ b/security/nss/cmd/selfserv/selfserv.c @@ -114,8 +114,12 @@ int ssl3CipherSuites[] = { 0 }; -int requestCert; +/* data and structures for shutdown */ int stopping; +PRLock * stopLock; +PRCondVar * stopQ; + +int requestCert; int verbose; SECItem bigBuf; @@ -144,7 +148,7 @@ Usage(const char *progName) { fprintf(stderr, -"Usage: %s -n rsa_nickname -p port [-3RTmrvx] [-w password]\n" +"Usage: %s -n rsa_nickname -p port [-3RTmrvx] [-w password] [-t threads]\n" " [-i pid_file] [-c ciphers] [-d dbdir] [-f fortezza_nickname] \n" "-3 means disable SSL v3\n" "-T means disable TLS\n" @@ -155,6 +159,7 @@ Usage(const char *progName) " 2 -r's mean request and require, cert on initial handshake.\n" " 3 -r's mean request, not require, cert on second handshake.\n" " 4 -r's mean request and require, cert on second handshake.\n" +"-t threads -- specify the number of threads to use for connections.\n" "-v means verbose output\n" "-x means use export policy.\n" "-i pid_file file to write the process id of selfserve\n" @@ -364,17 +369,20 @@ extern long ssl3_hch_sid_cache_not_ok; /************************************************************************** ** Begin thread management routines and data. **************************************************************************/ +#define MIN_THREADS 3 +#define DEFAULT_THREADS 8 +#define MAX_THREADS 128 +static int maxThreads = DEFAULT_THREADS; -#define MAX_THREADS 32 typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c); PRLock * threadLock; -PRCondVar * threadStartQ; -PRCondVar * threadEndQ; +PRCondVar * threadQ; -int numUsed; -int numRunning; +PRLock * stopLock; +PRCondVar * stopQ; +static int threadCount = 0; typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; @@ -385,29 +393,23 @@ typedef struct perThreadStr { int rv; startFn * startFunc; PRThread * prThread; - PRBool inUse; - runState running; + runState state; } perThread; -perThread threads[MAX_THREADS]; +perThread *threads; void thread_wrapper(void * arg) { perThread * slot = (perThread *)arg; - /* wait for parent to finish launching us before proceeding. */ - PR_Lock(threadLock); - PR_Unlock(threadLock); - slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); - PR_Lock(threadLock); - slot->running = rs_zombie; - /* notify the thread exit handler. */ - PR_NotifyCondVar(threadEndQ); - + PR_Lock(threadLock); + slot->state = rs_zombie; + --threadCount; + PR_NotifyAllCondVar(threadQ); PR_Unlock(threadLock); } @@ -420,116 +422,59 @@ launch_thread( { perThread * slot; int i; + static int highWaterMark = 0; + int workToDo = 1; - if (!threadStartQ) { - threadLock = PR_NewLock(); - threadStartQ = PR_NewCondVar(threadLock); - threadEndQ = PR_NewCondVar(threadLock); - } PR_Lock(threadLock); - while (numRunning >= MAX_THREADS) { - PR_WaitCondVar(threadStartQ, PR_INTERVAL_NO_TIMEOUT); - } - for (i = 0; i < numUsed; ++i) { - slot = threads + i; - if (slot->running == rs_idle) - break; - } - if (i >= numUsed) { - if (i >= MAX_THREADS) { - /* something's really wrong here. */ - PORT_Assert(i < MAX_THREADS); - PR_Unlock(threadLock); - return SECFailure; - } - ++numUsed; - PORT_Assert(numUsed == i + 1); - slot = threads + i; - } + /* for each perThread structure in the threads array */ + while( workToDo ) { + for (i = 0; i < maxThreads; ++i) { + slot = threads + i; - slot->a = a; - slot->b = b; - slot->c = c; + /* create new thread */ + if (( slot->state == rs_idle ) || ( slot->state == rs_zombie )) { + slot->state = 1; + workToDo = 0; + slot->a = a; + slot->b = b; + slot->c = c; + slot->startFunc = startFunc; + slot->prThread = PR_CreateThread(PR_USER_THREAD, thread_wrapper, slot, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (slot->prThread == NULL) { + printf("selfserv: Failed to launch thread!\n"); + slot->state = rs_idle; + return SECFailure; + } - slot->startFunc = startFunc; - - slot->prThread = PR_CreateThread(PR_USER_THREAD, - thread_wrapper, slot, - PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, - PR_JOINABLE_THREAD, 0); - if (slot->prThread == NULL) { - PR_Unlock(threadLock); - printf("selfserv: Failed to launch thread!\n"); - return SECFailure; - } - - slot->inUse = 1; - slot->running = 1; - ++numRunning; - PR_Unlock(threadLock); - PRINTF("selfserv: Launched thread in slot %d \n", i); - FLUSH; + highWaterMark = ( i > highWaterMark )? i : highWaterMark; + PRINTF("selfserv: Launched thread in slot %d, highWaterMark: %d \n", i, highWaterMark ); + FLUSH; + ++threadCount; + break; /* we did what we came here for, leave */ + } + } /* end for() */ + if ( workToDo ) + PR_WaitCondVar(threadQ, PR_INTERVAL_NO_TIMEOUT); + } /* end while() */ + PR_Unlock(threadLock); return SECSuccess; } -int -reap_threads(void) -{ - perThread * slot; - int i; - - if (!threadLock) - return 0; - PR_Lock(threadLock); - while (numRunning > 0) { - PR_WaitCondVar(threadEndQ, PR_INTERVAL_NO_TIMEOUT); - for (i = 0; i < numUsed; ++i) { - slot = threads + i; - if (slot->running == rs_zombie) { - /* Handle cleanup of thread here. */ - PRINTF("selfserv: Thread in slot %d returned %d\n", i, slot->rv); - - /* Now make sure the thread has ended OK. */ - PR_JoinThread(slot->prThread); - slot->running = rs_idle; - --numRunning; - - /* notify the thread launcher. */ - PR_NotifyCondVar(threadStartQ); - } - } - } - - /* Safety Sam sez: make sure count is right. */ - for (i = 0; i < numUsed; ++i) { - slot = threads + i; - if (slot->running != rs_idle) { - FPRINTF(stderr, "selfserv: Thread in slot %d is in state %d!\n", - i, slot->running); - } - } - PR_Unlock(threadLock); - FLUSH; - return 0; -} - void destroy_thread_data(void) { PORT_Memset(threads, 0, sizeof threads); - if (threadEndQ) { - PR_DestroyCondVar(threadEndQ); - threadEndQ = NULL; - } - if (threadStartQ) { - PR_DestroyCondVar(threadStartQ); - threadStartQ = NULL; + if (threadQ) { + PR_DestroyCondVar(threadQ); + threadQ = NULL; } if (threadLock) { - PR_DestroyLock(threadLock); - threadLock = NULL; + PR_DestroyLock(threadLock); + threadLock = NULL; } } @@ -991,6 +936,13 @@ server_main( PRSocketOptionData opt; networkStart(); + + /* create the thread management serialization structs */ + threadLock = PR_NewLock(); + threadQ = PR_NewCondVar(threadLock); + stopLock = PR_NewLock(); + stopQ = PR_NewCondVar(stopLock); + addr.inet.family = PR_AF_INET; addr.inet.ip = PR_INADDR_ANY; @@ -1113,7 +1065,11 @@ server_main( if (rv != SECSuccess) { PR_Close(listen_sock); } else { - reap_threads(); + PR_Lock( stopLock ); + while ( !stopping && threadCount > 0 ) { + PR_WaitCondVar(stopQ, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock( stopLock ); destroy_thread_data(); } @@ -1187,7 +1143,7 @@ main(int argc, char **argv) progName = strrchr(tmp, '\\'); progName = progName ? progName + 1 : tmp; - optstate = PL_CreateOptState(argc, argv, "RT2:3c:d:p:mn:i:f:rvw:x"); + optstate = PL_CreateOptState(argc, argv, "RT2:3c:d:p:mn:i:f:rt:vw:x"); while (PL_GetNextOpt(optstate) == PL_OPT_OK) { switch(optstate->option) { default: @@ -1217,6 +1173,12 @@ main(int argc, char **argv) case 'r': ++requestCert; break; + case 't': + maxThreads = PORT_Atoi(optstate->value); + if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS; + if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS; + break; + case 'v': verbose++; break; case 'w': passwd = optstate->value; break; @@ -1225,6 +1187,13 @@ main(int argc, char **argv) } } + /* allocate the array of thread slots */ + threads = PR_Calloc(maxThreads, sizeof(perThread)); + if ( NULL == threads ) { + fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n"); + goto mainExit; + } + if ((nickName == NULL) && (fNickName == NULL)) Usage(progName); @@ -1321,6 +1290,7 @@ main(int argc, char **argv) server_main(port, requestCert, privKey, cert); +mainExit: NSS_Shutdown(); PR_Cleanup(); return 0;