michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /*********************************************************************** michael@0: ** 1996 - Netscape Communications Corporation michael@0: ** michael@0: ** Name: cvar2.c michael@0: ** michael@0: ** Description: Simple test creates several local and global threads; michael@0: ** half use a single,shared condvar, and the michael@0: ** other half have their own condvar. The main thread then loops michael@0: ** notifying them to wakeup. michael@0: ** michael@0: ** Modification History: michael@0: ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. michael@0: ** The debug mode will print all of the printfs associated with this test. michael@0: ** The regress mode will be the default mode. Since the regress tool limits michael@0: ** the output to a one line status:PASS or FAIL,all of the printf statements michael@0: ** have been handled with an if (debug_mode) statement. michael@0: ***********************************************************************/ michael@0: michael@0: #include "nspr.h" michael@0: #include "plerror.h" michael@0: #include "plgetopt.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: int _debug_on = 0; michael@0: #define DPRINTF(arg) if (_debug_on) printf arg michael@0: michael@0: #define DEFAULT_COUNT 100 michael@0: #define DEFAULT_THREADS 5 michael@0: PRInt32 count = DEFAULT_COUNT; michael@0: michael@0: typedef struct threadinfo { michael@0: PRThread *thread; michael@0: PRInt32 id; michael@0: PRBool internal; michael@0: PRInt32 *tcount; michael@0: PRLock *lock; michael@0: PRCondVar *cvar; michael@0: PRIntervalTime timeout; michael@0: PRInt32 loops; michael@0: michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: PRInt32 *exitcount; michael@0: } threadinfo; michael@0: michael@0: /* michael@0: ** Make exitcount, tcount static. for Win16. michael@0: */ michael@0: static PRInt32 exitcount=0; michael@0: static PRInt32 tcount=0; michael@0: michael@0: michael@0: /* Thread that gets notified; many threads share the same condvar */ michael@0: void PR_CALLBACK michael@0: SharedCondVarThread(void *_info) michael@0: { michael@0: threadinfo *info = (threadinfo *)_info; michael@0: PRInt32 index; michael@0: michael@0: for (index=0; indexloops; index++) { michael@0: PR_Lock(info->lock); michael@0: if (*info->tcount == 0) michael@0: PR_WaitCondVar(info->cvar, info->timeout); michael@0: #if 0 michael@0: printf("shared thread %ld notified in loop %ld\n", info->id, index); michael@0: #endif michael@0: (*info->tcount)--; michael@0: PR_Unlock(info->lock); michael@0: michael@0: PR_Lock(info->exitlock); michael@0: (*info->exitcount)++; michael@0: PR_NotifyCondVar(info->exitcvar); michael@0: PR_Unlock(info->exitlock); michael@0: } michael@0: #if 0 michael@0: printf("shared thread %ld terminating\n", info->id); michael@0: #endif michael@0: } michael@0: michael@0: /* Thread that gets notified; no other threads use the same condvar */ michael@0: void PR_CALLBACK michael@0: PrivateCondVarThread(void *_info) michael@0: { michael@0: threadinfo *info = (threadinfo *)_info; michael@0: PRInt32 index; michael@0: michael@0: for (index=0; indexloops; index++) { michael@0: PR_Lock(info->lock); michael@0: if (*info->tcount == 0) { michael@0: DPRINTF(("PrivateCondVarThread: thread 0x%lx waiting on cvar = 0x%lx\n", michael@0: PR_GetCurrentThread(), info->cvar)); michael@0: PR_WaitCondVar(info->cvar, info->timeout); michael@0: } michael@0: #if 0 michael@0: printf("solo thread %ld notified in loop %ld\n", info->id, index); michael@0: #endif michael@0: (*info->tcount)--; michael@0: PR_Unlock(info->lock); michael@0: michael@0: PR_Lock(info->exitlock); michael@0: (*info->exitcount)++; michael@0: PR_NotifyCondVar(info->exitcvar); michael@0: DPRINTF(("PrivateCondVarThread: thread 0x%lx notified exitcvar = 0x%lx cnt = %ld\n", michael@0: PR_GetCurrentThread(), info->exitcvar,(*info->exitcount))); michael@0: PR_Unlock(info->exitlock); michael@0: } michael@0: #if 0 michael@0: printf("solo thread %ld terminating\n", info->id); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: CreateTestThread(threadinfo *info, michael@0: PRInt32 id, michael@0: PRLock *lock, michael@0: PRCondVar *cvar, michael@0: PRInt32 loops, michael@0: PRIntervalTime timeout, michael@0: PRInt32 *tcount, michael@0: PRLock *exitlock, michael@0: PRCondVar *exitcvar, michael@0: PRInt32 *exitcount, michael@0: PRBool shared, michael@0: PRThreadScope scope) michael@0: { michael@0: info->id = id; michael@0: info->internal = (shared) ? PR_FALSE : PR_TRUE; michael@0: info->lock = lock; michael@0: info->cvar = cvar; michael@0: info->loops = loops; michael@0: info->timeout = timeout; michael@0: info->tcount = tcount; michael@0: info->exitlock = exitlock; michael@0: info->exitcvar = exitcvar; michael@0: info->exitcount = exitcount; michael@0: info->thread = PR_CreateThread( michael@0: PR_USER_THREAD, michael@0: shared?SharedCondVarThread:PrivateCondVarThread, michael@0: info, michael@0: PR_PRIORITY_NORMAL, michael@0: scope, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: if (!info->thread) michael@0: PL_PrintError("error creating thread\n"); michael@0: } michael@0: michael@0: michael@0: void michael@0: CondVarTestSUU(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: michael@0: exitcount=0; michael@0: tcount=0; michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg); michael@0: exitcount -= arg; michael@0: PR_Unlock(exitlock); michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg); index++) michael@0: PR_JoinThread(list[index].thread); michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: } michael@0: michael@0: void michael@0: CondVarTestSUK(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: exitcount=0; michael@0: tcount=0; michael@0: michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg); michael@0: exitcount -= arg; michael@0: PR_Unlock(exitlock); michael@0: #if 0 michael@0: printf("threads ready\n"); michael@0: #endif michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg); index++) michael@0: PR_JoinThread(list[index].thread); michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: } michael@0: michael@0: void michael@0: CondVarTestPUU(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: PRInt32 *tcount, *saved_tcount; michael@0: michael@0: exitcount=0; michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg); michael@0: exitcount -= arg; michael@0: PR_Unlock(exitlock); michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg); index++) { michael@0: DPRINTF(("CondVarTestPUU: joining thread 0x%lx\n",list[index].thread)); michael@0: PR_JoinThread(list[index].thread); michael@0: if (list[index].internal) { michael@0: PR_Lock(list[index].lock); michael@0: PR_DestroyCondVar(list[index].cvar); michael@0: PR_Unlock(list[index].lock); michael@0: PR_DestroyLock(list[index].lock); michael@0: } michael@0: } michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: PR_DELETE(saved_tcount); michael@0: } michael@0: michael@0: void michael@0: CondVarTestPUK(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: PRInt32 *tcount, *saved_tcount; michael@0: michael@0: exitcount=0; michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg); michael@0: exitcount -= arg; michael@0: PR_Unlock(exitlock); michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg); index++) { michael@0: PR_JoinThread(list[index].thread); michael@0: if (list[index].internal) { michael@0: PR_Lock(list[index].lock); michael@0: PR_DestroyCondVar(list[index].cvar); michael@0: PR_Unlock(list[index].lock); michael@0: PR_DestroyLock(list[index].lock); michael@0: } michael@0: } michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: PR_DELETE(saved_tcount); michael@0: } michael@0: michael@0: void michael@0: CondVarTest(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: PRInt32 *ptcount, *saved_ptcount; michael@0: michael@0: exitcount=0; michael@0: tcount=0; michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: saved_ptcount = ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg*4); michael@0: exitcount -= arg*4; michael@0: PR_Unlock(exitlock); michael@0: #if 0 michael@0: printf("threads ready\n"); michael@0: #endif michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg*4); index++) { michael@0: PR_JoinThread(list[index].thread); michael@0: if (list[index].internal) { michael@0: PR_Lock(list[index].lock); michael@0: PR_DestroyCondVar(list[index].cvar); michael@0: PR_Unlock(list[index].lock); michael@0: PR_DestroyLock(list[index].lock); michael@0: } michael@0: } michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: PR_DELETE(saved_ptcount); michael@0: } michael@0: michael@0: void michael@0: CondVarTimeoutTest(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg*4); michael@0: exitcount -= arg*4; michael@0: PR_Unlock(exitlock); michael@0: } michael@0: michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg*4); index++) { michael@0: PR_JoinThread(list[index].thread); michael@0: if (list[index].internal) { michael@0: PR_Lock(list[index].lock); michael@0: PR_DestroyCondVar(list[index].cvar); michael@0: PR_Unlock(list[index].lock); michael@0: PR_DestroyLock(list[index].lock); michael@0: } michael@0: } michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: PR_DestroyCondVar(exitcvar); michael@0: PR_DestroyLock(exitlock); michael@0: michael@0: PR_DELETE(list); michael@0: } michael@0: michael@0: void michael@0: CondVarMixedTest(void *_arg) michael@0: { michael@0: PRInt32 arg = (PRInt32)_arg; michael@0: PRInt32 index, loops; michael@0: threadinfo *list; michael@0: PRLock *sharedlock; michael@0: PRCondVar *sharedcvar; michael@0: PRLock *exitlock; michael@0: PRCondVar *exitcvar; michael@0: PRInt32 *ptcount; michael@0: michael@0: exitcount=0; michael@0: tcount=0; michael@0: list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); michael@0: ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4)); michael@0: michael@0: sharedlock = PR_NewLock(); michael@0: sharedcvar = PR_NewCondVar(sharedlock); michael@0: exitlock = PR_NewLock(); michael@0: exitcvar = PR_NewCondVar(exitlock); michael@0: michael@0: /* Create the threads */ michael@0: for(index=0; index= arg*4); michael@0: exitcount -= arg*4; michael@0: PR_Unlock(exitlock); michael@0: } michael@0: michael@0: /* Join all the threads */ michael@0: for(index=0; index<(arg*4); index++) { michael@0: PR_JoinThread(list[index].thread); michael@0: if (list[index].internal) { michael@0: PR_Lock(list[index].lock); michael@0: PR_DestroyCondVar(list[index].cvar); michael@0: PR_Unlock(list[index].lock); michael@0: PR_DestroyLock(list[index].lock); michael@0: } michael@0: } michael@0: michael@0: PR_DestroyCondVar(sharedcvar); michael@0: PR_DestroyLock(sharedlock); michael@0: michael@0: PR_DELETE(list); michael@0: } michael@0: michael@0: void michael@0: CondVarCombinedTest(void *arg) michael@0: { michael@0: PRThread *threads[3]; michael@0: michael@0: threads[0] = PR_CreateThread(PR_USER_THREAD, michael@0: CondVarTest, michael@0: (void *)arg, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: threads[1] = PR_CreateThread(PR_USER_THREAD, michael@0: CondVarTimeoutTest, michael@0: (void *)arg, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: threads[2] = PR_CreateThread(PR_USER_THREAD, michael@0: CondVarMixedTest, michael@0: (void *)arg, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: michael@0: PR_JoinThread(threads[0]); michael@0: PR_JoinThread(threads[1]); michael@0: PR_JoinThread(threads[2]); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: static void Measure(void (*func)(void *), PRInt32 arg, const char *msg) michael@0: { michael@0: PRIntervalTime start, stop; michael@0: double d; michael@0: michael@0: start = PR_IntervalNow(); michael@0: (*func)((void *)arg); michael@0: stop = PR_IntervalNow(); michael@0: michael@0: d = (double)PR_IntervalToMicroseconds(stop - start); michael@0: michael@0: printf("%40s: %6.2f usec\n", msg, d / count); michael@0: } michael@0: michael@0: static PRIntn PR_CALLBACK RealMain(int argc, char **argv) michael@0: { michael@0: PRInt32 threads, default_threads = DEFAULT_THREADS; michael@0: PLOptStatus os; michael@0: PLOptState *opt = PL_CreateOptState(argc, argv, "vc:t:"); michael@0: while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) michael@0: { michael@0: if (PL_OPT_BAD == os) continue; michael@0: switch (opt->option) michael@0: { michael@0: case 'v': /* debug mode */ michael@0: _debug_on = 1; michael@0: break; michael@0: case 'c': /* loop counter */ michael@0: count = atoi(opt->value); michael@0: break; michael@0: case 't': /* number of threads involved */ michael@0: default_threads = atoi(opt->value); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: PL_DestroyOptState(opt); michael@0: michael@0: if (0 == count) count = DEFAULT_COUNT; michael@0: if (0 == default_threads) default_threads = DEFAULT_THREADS; michael@0: michael@0: printf("\n\ michael@0: CondVar Test: \n\ michael@0: \n\ michael@0: Simple test creates several local and global threads; half use a single,\n\ michael@0: shared condvar, and the other half have their own condvar. The main \n\ michael@0: thread then loops notifying them to wakeup. \n\ michael@0: \n\ michael@0: The timeout test is very similar except that the threads are not \n\ michael@0: notified. They will all wakeup on a 1 second timeout. \n\ michael@0: \n\ michael@0: The mixed test combines the simple test and the timeout test; every \n\ michael@0: third thread is notified, the other threads are expected to timeout \n\ michael@0: correctly. \n\ michael@0: \n\ michael@0: Lastly, the combined test creates a thread for each of the above three \n\ michael@0: cases and they all run simultaneously. \n\ michael@0: \n\ michael@0: This test is run with %d, %d, %d, and %d threads of each type.\n\n", michael@0: default_threads, default_threads*2, default_threads*3, default_threads*4); michael@0: michael@0: PR_SetConcurrency(2); michael@0: michael@0: for (threads = default_threads; threads < default_threads*5; threads+=default_threads) { michael@0: printf("\n%ld Thread tests\n", threads); michael@0: Measure(CondVarTestSUU, threads, "Condvar simple test shared UU"); michael@0: Measure(CondVarTestSUK, threads, "Condvar simple test shared UK"); michael@0: Measure(CondVarTestPUU, threads, "Condvar simple test priv UU"); michael@0: Measure(CondVarTestPUK, threads, "Condvar simple test priv UK"); michael@0: Measure(CondVarTest, threads, "Condvar simple test All"); michael@0: Measure(CondVarTimeoutTest, threads, "Condvar timeout test"); michael@0: #if 0 michael@0: Measure(CondVarMixedTest, threads, "Condvar mixed timeout test"); michael@0: Measure(CondVarCombinedTest, threads, "Combined condvar test"); michael@0: #endif michael@0: } michael@0: michael@0: printf("PASS\n"); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRIntn rv; michael@0: michael@0: PR_STDIO_INIT(); michael@0: rv = PR_Initialize(RealMain, argc, argv, 0); michael@0: return rv; michael@0: } /* main */