/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is AOLserver Code and related documentation distributed by * AOL. * * The Initial Developer of the Original Code is America Online, Inc. Portions * created by AOL are Copyright (C) 1999 America Online, Inc. All Rights * Reserved. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License (the "GPL"), in which case the provisions of * GPL are applicable instead of those above. If you wish to allow use of * your version of this file only under the terms of the GPL and not to allow * others to use your version of this file under the License, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the License or the GPL. */ /* * test.c - * * Collection of thread interface tests. This code is somewhat sloppy * but contains several examples of of using conditions, mutexes, * thread local storage, and creating/joining threads. */ #include "nsthread.h" /* * Special direct include of pthread.h for compatibility tests. */ #ifdef _WIN32 #define PTHREAD_TEST 0 #else #include #define PTHREAD_TEST 1 #endif static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsthread/nsthreadtest.c,v 1.6 2005/08/08 11:30:51 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; /* * Collection of synchronization objects for tests. */ static Ns_Mutex block; static Ns_Mutex mlock; static Ns_Mutex lock; static Ns_Cond cond; static Ns_Tls key; static Ns_RWLock rwlock; static Ns_Sema sema; static Ns_Cs cs; static Ns_Mutex dlock; static Ns_Cond dcond; static int dstop; /* * Msg - * * Simple message logger with thread id and name. */ void Msg(char *fmt,...) { va_list ap; char *s, *r; time_t now; time(&now); s = ns_ctime(&now); r = strchr(s, '\n'); if (r) { *r = '\0'; } va_start(ap, fmt); Ns_MutexLock(&mlock); printf("[%s][%s]: ", Ns_ThreadGetName(), s); vfprintf(stdout, fmt, ap); printf("\n"); Ns_MutexUnlock(&mlock); va_end(ap); } /* * TlsLogArg - * * Log and then free TLS slot data at thread exit. */ void TlsLogArg(void *arg) { int *ip = arg; Msg("tls cleanup %d", *ip); ns_free(ip); } /* * RecursiveStackCheck, CheckStackThread - * * Thread which recursively probes stack for max depth. */ int RecursiveStackCheck(int n) { if (Ns_CheckStack() == NS_OK) { n = RecursiveStackCheck(n); } ++n; return n; } void CheckStackThread(void *arg) { int n; Ns_ThreadSetName("checkstack"); n = RecursiveStackCheck(0); Ns_ThreadExit((void *) n); } /* * WorkThread - * * Thread which exercies a varity of sync objects and TLS. */ void WorkThread(void *arg) { int i = (int) arg; int *ip; time_t now; Ns_Thread self; char name[32]; sprintf(name, "-work:%d-", i); Ns_ThreadSetName(name); if (i == 2) { Ns_RWLockWrLock(&rwlock); Msg("rwlock write aquired"); sleep(2); } else { Ns_RWLockRdLock(&rwlock); Msg("rwlock read aquired aquired"); sleep(1); } Ns_CsEnter(&cs); Msg("enter critical section once"); Ns_CsEnter(&cs); Msg("enter critical section twice"); Ns_CsLeave(&cs); Ns_CsLeave(&cs); Ns_ThreadSelf(&self); arg = Ns_TlsGet(&key); Ns_SemaWait(&sema); Msg("got semaphore posted from main"); if (arg == NULL) { arg = ns_malloc(sizeof(int)); Ns_TlsSet(&key, arg); } ip = arg; *ip = i; if (i == 5) { Ns_Time to; int st; Ns_GetTime(&to); Msg("time: %ld %ld", to.sec, to.usec); Ns_IncrTime(&to, 5, 0); Msg("time: %ld %ld", to.sec, to.usec); Ns_MutexLock(&lock); time(&now); Msg("timed wait starts: %s", ns_ctime(&now)); st = Ns_CondTimedWait(&cond, &lock, &to); Ns_MutexUnlock(&lock); time(&now); Msg("timed wait ends: %s - status: %d", ns_ctime(&now), st); } if (i == 9) { Msg("sleep 4 seconds start"); sleep(4); Msg("sleep 4 seconds done"); } time(&now); Ns_RWLockUnlock(&rwlock); Msg("rwlock unlocked"); Msg("exiting"); Ns_ThreadExit((void *) i); } /* * AtExit - * * Test of atexit() handler. */ void AtExit(void) { Msg("atexit handler called!"); } /* * MemThread, MemTime - * * Time allocations of malloc and zippy ns_malloc. */ #define NA 10000 int nthreads = 10; int memstart; int nrunning; void MemThread(void *arg) { int i; void *ptr; Ns_ThreadSetName("memthread"); Ns_MutexLock(&lock); ++nrunning; Ns_CondBroadcast(&cond); while (!memstart) { Ns_CondWait(&cond, &lock); } Ns_MutexUnlock(&lock); ptr = NULL; for (i = 0; i < NA; ++i) { if (arg) { if (ptr) ns_free(ptr); ptr = ns_malloc(10); } else { if (ptr) free(ptr); ptr = malloc(10); } } } void MemTime(int ns) { Ns_Time start, end, diff; int i; Ns_Thread *tids; tids = ns_malloc(sizeof(Ns_Thread *) * nthreads); Ns_MutexLock(&lock); nrunning = 0; memstart = 0; Ns_MutexUnlock(&lock); printf("starting %d %smalloc threads...", nthreads, ns ? "ns_" : ""); fflush(stdout); Ns_GetTime(&start); for (i = 0; i < nthreads; ++i) { Ns_ThreadCreate(MemThread, (void *) ns, 0, &tids[i]); } Ns_MutexLock(&lock); while (nrunning < nthreads) { Ns_CondWait(&cond, &lock); } printf("waiting...."); fflush(stdout); memstart = 1; Ns_CondBroadcast(&cond); Ns_MutexUnlock(&lock); for (i = 0; i < nthreads; ++i) { Ns_ThreadJoin(&tids[i], NULL); } Ns_GetTime(&end); Ns_DiffTime(&end, &start, &diff); printf("done: %d seconds, %d usec\n", (int) diff.sec, (int) diff.usec); } void DumpString(Tcl_DString *dsPtr) { char **largv; int i, largc; if (Tcl_SplitList(NULL, dsPtr->string, &largc, (CONST char***)&largv) == TCL_OK) { for (i = 0; i < largc; ++i) { printf("\t%s\n", largv[i]); } ckfree((char *) largv); } Tcl_DStringTrunc(dsPtr, 0); } void DumperThread(void *arg) { Ns_Time to; Tcl_DString ds; Tcl_DStringInit(&ds); Ns_ThreadSetName("-dumper-"); Ns_MutexLock(&block); Ns_MutexLock(&dlock); while (!dstop) { Ns_GetTime(&to); Ns_IncrTime(&to, 1, 0); Ns_CondTimedWait(&dcond, &dlock, &to); Ns_MutexLock(&mlock); Ns_ThreadList(&ds, NULL); DumpString(&ds); Ns_MutexList(&ds); DumpString(&ds); #if !defined(_WIN32) && defined(USE_THREAD_ALLOC) && (STATIC_BUILD == 0) /* NB: Not yet exported in WIN32 Tcl. */ Tcl_GetMemoryInfo(&ds); #endif DumpString(&ds); Ns_MutexUnlock(&mlock); } Ns_MutexUnlock(&dlock); Ns_MutexUnlock(&block); } static void DetachedThread(void *ignored) { int i; for (i = 0; i < 10; ++i) { Msg("\n\ndetached! %d", i); sleep(1); } } #if PTHREAD_TEST /* * Routines to test compatibility with pthread-created * threads, i.e., that non-Ns_ThreadCreate'd threads * can call Ns API's which will cleanup at thread exit. */ static Ns_Mutex plock; static Ns_Cond pcond; static int pgo; void PthreadTlsCleanup(void *arg) { int i = (int) arg; printf("pthread[%d]: log: %d\n", (int) pthread_self(), i); } void * Pthread(void *arg) { static Ns_Tls tls; /* * Allocate TLS first time (this is recommended TLS * self-initialization style. */ Ns_ThreadSetName("pthread"); sleep(5); if (tls == NULL) { Ns_MasterLock(); if (tls == NULL) { Ns_TlsAlloc(&tls, PthreadTlsCleanup); } Ns_MasterUnlock(); } Ns_TlsSet(&tls, arg); /* * Wait for exit signal from main(). */ Ns_MutexLock(&plock); while (!pgo) { Ns_CondWait(&pcond, &plock); } Ns_MutexUnlock(&plock); return arg; } #endif /* * main - * * Fire off a bunch of weird threads to exercise the thread * interface. */ int main(int argc, char *argv[]) { int i, code; Ns_Thread threads[10]; Ns_Thread self, dumper; void *arg; char *p; #if PTHREAD_TEST pthread_t tids[10]; #endif NsThreads_LibInit(); Ns_ThreadSetName("-main-"); /* * Jump directly to memory test if requested. */ for (i = 1; i < argc; ++i) { p = argv[i]; switch (*p) { case 'n': break; case 'm': nthreads = atoi(p + 1); goto mem; break; } } Ns_ThreadCreate(DetachedThread, NULL, 0, NULL); Ns_ThreadCreate(DumperThread, NULL, 0, &dumper); Ns_MutexSetName(&lock, "startlock"); Ns_MutexSetName(&dlock, "dumplock"); Ns_MutexSetName(&mlock, "msglock"); Ns_MutexSetName(&block, "busylock"); Ns_ThreadStackSize(81920); Ns_SemaInit(&sema, 3); Msg("sema initialized to 3"); atexit(AtExit); Msg("pid = %d", getpid()); Ns_TlsAlloc(&key, TlsLogArg); for (i = 0; i < 10; ++i) { Msg("starting work thread %d", i); Ns_ThreadCreate(WorkThread, (void *) i, 0, &threads[i]); } sleep(1); /* Ns_CondSignal(&cond); */ Ns_SemaPost(&sema, 10); Msg("sema post 10"); Ns_RWLockWrLock(&rwlock); Msg("rwlock write locked (main thread)"); sleep(1); Ns_RWLockUnlock(&rwlock); Msg("rwlock write unlocked (main thread)"); for (i = 0; i < 10; ++i) { Msg("waiting for thread %d to exit", i); Ns_ThreadJoin(&threads[i], (void **) &code); Msg("thread %d exited - code: %d", i, code); } #if PTHREAD_TEST for (i = 0; i < 10; ++i) { pthread_create(&tids[i], NULL, Pthread, (void *) i); printf("pthread: create %d = %d\n", i, (int) tids[i]); Ns_ThreadYield(); } Ns_MutexLock(&plock); pgo = 1; Ns_MutexUnlock(&plock); Ns_CondBroadcast(&pcond); for (i = 0; i < 10; ++i) { pthread_join(tids[i], &arg); printf("pthread: join %d = %d\n", i, (int) arg); } #endif Ns_ThreadSelf(&self); Ns_MutexLock(&dlock); dstop = 1; Ns_CondSignal(&dcond); Ns_MutexUnlock(&dlock); Ns_ThreadJoin(&dumper, NULL); Msg("threads joined"); for (i = 0; i < 10; ++i) { Ns_ThreadCreate(CheckStackThread, NULL, 8192*(i+1), &threads[i]); } for (i = 0; i < 10; ++i) { Ns_ThreadJoin(&threads[i], &arg); printf("check stack %d = %d\n", i, (int) arg); } /*Ns_ThreadEnum(DumpThreads, NULL);*/ /*Ns_MutexEnum(DumpLocks, NULL);*/ mem: MemTime(0); MemTime(1); return 0; }