/*** * mtest.c - Multi-thread debug testing module * * Copyright (c) 1987-2001, Microsoft Corporation. All rights reserved. * *Purpose: * This source contains a group of routines used for multi-thread * testing. In order to use the debug flavor of these routines, you * MUST link special debug versions of multi-thread crt0dat.obj and * mlock.obj into your program. * * [NOTE: This source module is NOT included in the C runtime library; * it is used only for testing and must be explicitly linked into the * test program.] * *Revision History: * 12-??-87 JCR Module created. * 06-17-88 JCR Misc. bug fixes. * 08-03-88 JCR Use the stdio.h value of _NFILE * 10-03-88 JCR 386: Use SYS calls, not DOS calls * 10-04-88 JCR 386: Removed 'far' keyword * 10-10-88 GJF Made API names match DOSCALLS.H * 06-08-89 JCR New 386 _beginthread interface; also brought * lots of new options across from the C600 tree. * 07-11-89 JCR Added _POPEN_LOCK to _locknames[] array * 07-14-89 JCR Added _LOCKTAB_LOCK support * 07-24-90 SBM Removed '32' from API names * 09-06-94 CFW Change M_I386 to _M_IX86. * *******************************************************************************/ #ifdef _M_IX86 #ifdef STACKALLOC #error Can't define STACKALLOC in 386 mode #endif #endif #ifdef _M_IX86 #ifdef _DOSCREATETHREAD_ #error Currently can't define _DOSCREATETHREAD_ in 386 mode #endif #endif #ifdef _DOSCREATETHREAD_ #ifndef STACKALLOC #error Can't define _DOSCREATETHREAD_ without STACKALLOC #endif #endif /* Multi-thread core tester module. */ #include #include #include #include #include #include #include #ifdef DEBUG #include #include #endif /* Define FAR to be blank for the 386 and far otherwise. */ #undef FAR #ifdef _M_IX86 #define FAR #else #define FAR far #endif /* define stack size */ #ifdef _M_IX86 #define _STACKSIZE_ 8192 #else #define _STACKSIZE_ 2048 #endif /* routines */ #ifdef _M_IX86 unsigned _syscall DOSSLEEP (unsigned long) ; #else unsigned FAR pascal DOSSLEEP (unsigned long) ; #endif int main ( int argc , char * * argv ) ; int minit(void); void childcode ( void FAR * arg ) ; #ifdef _DOSCREATETHREAD_ #ifndef _M_IX86 void childcode ( void ) ; unsigned FAR pascal DOSCREATETHREAD (void FAR *, void FAR *, void FAR *); #endif #else void childcode ( void FAR * arg ) ; #endif int mterm(void); /* global data */ char Result [ _THREADMAX_ ] ; unsigned Synchronize ; #ifdef DEBUG /* Array of lock names. This order must match the declarations in mtdll.h and mtdll.inc. */ char *_locknames[] = { "** NO LOCK 0 ** ", /* lock values are 1-based */ "_SIGNAL_LOCK ", "_IOB_SCAN_LOCK ", "_TMPNAM_LOCK ", "_INPUT_LOCK ", "_OUTPUT_LOCK ", "_CSCANF_LOCK ", "_CPRINTF_LOCK ", "_CONIO_LOCK ", "_HEAP_LOCK ", "_BHEAP_LOCK ", "_TIME_LOCK ", "_ENV_LOCK ", "_EXIT_LOCK1 ", "_EXIT_LOCK2 ", "_THREADDATA_LOCK", "_POPEN_LOCK ", "_SSCANF_LOCK ", "_SPRINTF_LOCK ", #ifdef _M_IX86 "_VSPRINTF_LOCK ", "_LOCKTAB_LOCK " #else "_VSPRINTF_LOCK " #endif }; /* Minimal sanity check on above array. */ #ifdef _M_IX86 #if ((_LOCKTAB_LOCK+1)-_STREAM_LOCKS) #error *** _locknames[] does agree with lock values *** #endif #else /* !_M_IX86 */ #if ((_VSPRINTF_LOCK+1)-_STREAM_LOCKS) #error *** _locknames[] does agree with lock values *** #endif #endif /* _M_IX86 */ #endif /* DEBUG */ /*** * main() - Main mthread testing shell * *Purpose: * Provides a general purpose shell for mthread testing. * The module does the following: * * (1) Call minit() to perform test initialization operations. * * (2) Begins one thread for each argument passed to the * program. Each thread is passed the corresponding argument. * Thread begin location is assumed to be at routine childcode(); * * (3) Waits for all threads to terminate. * * (4) Calls mterm() to perform termination operations. * * Note that minit(), childcode(), and mterm() are routines that * are external to this source. Again, this source doesn't care * what their purpose or operation is. * * Also, childcode() is expected to conform to the following rules: * * (1) The childcode should not start running until * the variable 'Synchronize' becomes non-zero. * * (2) When the thread is done executing, it should set * the value Result[threadid] to a non-zero value so the * parent (i.e., this routine) knows it has completed. * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ int main ( int argc , char * * argv ) { int rc ; unsigned result = 0 ; long ChildCount ; int NumThreads ; int t ; int r ; int MaxThread = 0 ; long LoopCount ; #ifdef THREADLOOP char **argvsave; #endif #ifndef _M_IX86 char * stackbottom ; #endif #ifdef DEBUG if ( argc > MAXTHREADID) { printf("*** ERROR: Mthread debugging only supports %u threads ***\n", MAXTHREADID); return(-1); } #endif if ( -- argc > (_THREADMAX_-1) ) { printf ( "*** Error: Too many arguments***\n" ) ; return (-1) ; } /* Call the initiation routine */ if (minit() != 0) { printf("*** Error: From minit() routine ***\n"); return(-1); } /* Bring up the threads */ printf ( "Process ID = %u, Thread ID = %d, ArgCount= %d\r\n" , getpid ( ) , * _threadid , argc ) ; #ifndef _M_IX86 #ifdef STACKALLOC printf( "(thread stacks allocated explicilty by mtest suite)\r\n"); #else printf( "(thread stacks allocated implicitly via _beginthread)\r\n"); #endif #endif #ifdef THREADLOOP /* Bring up all the threads several times (so tids get re-used) */ argvsave=argv; for (threadloop=1;threadloop<=_THREADLOOPCNT_;threadloop++) { printf("\nThreadloop = %i\n", threadloop); argv=argvsave; #endif NumThreads = 0 ; while ( * ++ argv ) { ChildCount = atol ( * argv ) ; #ifdef _M_IX86 rc = _beginthread ( (void FAR *) childcode , _STACKSIZE_ , (void FAR *) ChildCount ) ; if ( rc == -1 ) #else /* !_M_IX86 */ #ifdef STACKALLOC if ( ! ( stackbottom = _fmalloc ( _STACKSIZE_ ) ) ) { printf ( "*** Error: Could not allocate a stack ***\n" ) ; break ; } #else stackbottom = (void FAR *) NULL; #endif #ifdef _DOSCREATETHREAD_ stackbottom+=_STACKSIZE_-16; /* point to end of malloc'd block */ rc1 = DOSCREATETHREAD( (void FAR *) childcode, &rc, (void FAR *) stackbottom); if (rc1 != 0) #else rc = _beginthread ( (void FAR *) childcode , (void FAR *) stackbottom , _STACKSIZE_ , (void FAR *) ChildCount ) ; if ( rc == -1 ) #endif #endif /* _M_IX86 */ { printf ("*** Error: Could not Spawn %d-th Thread (argument=%ld) ***\n" , NumThreads + 1 , ChildCount ) ; break ; } if ( rc > MaxThread ) MaxThread = rc ; printf ( "Spawning %d-th Thread %d with argument=%ld\r\n" , ++ NumThreads , rc , ChildCount ) ; } printf ( "NumThreads = %d, MaxThread = %d\r\n" , NumThreads, MaxThread ) ; /* Let the threads begin and wait for them to term. */ LoopCount = 0L ; Synchronize = 1 ; for ( t = 0 ; t < NumThreads ; ++ t ) { r = 0 ; while ( ! Result [ r ] ) { DOSSLEEP ( 0L ) ; if ( ++ r > MaxThread ) { r = 0 ; printf ( "%ld\r" , LoopCount ++ ) ; } } printf ( "%d: Thread %d Done.\r\n" , t , r) ; Result [ r ] = '\0' ; } #ifdef THREADLOOP } #endif /* All the threads have completed. Call the term routine and return. */ if (mterm() != 0) { printf("*** Error: From mterm() routine ***\n"); return(-1); } printf("\nDone!\n"); return 0 ; } #ifdef DEBUG /*** * Debug Print Routines - Display useful mthread lock data * *Purpose: * The following routines extract information from the multi-thread * debug data bases and print them out in various formats. * In order to use these routines, you MUST link special debug * versions of multi-thread crt0dat.obj and mlock.obj into your program. * *Entry: * *Exit: * 0 = success * 0! = failure * *Exceptions: * *******************************************************************************/ /*--- Print lock routine ---*/ int printlock(int locknum) { int retval; #ifdef _INIT_LOCKS if (locknum >= _STREAM_LOCKS) printf("\nValidating lock #%i (%s):\n",locknum, "not a 'single lock'"); else printf("\nValidating lock #%i: %s\n",locknum, _locknames[locknum]); #else printf("\nValidating lock #%i (%s, %s):\n", locknum, (locknum >= _STREAM_LOCKS ? "not a 'single' lock" : _locknames[locknum]), (_lock_exist(locknum) ? "initialized" : "NOT initialized") ); #endif retval = _check_lock(locknum); printf("\tLock count = %u\r\n", _lock_cnt(locknum)); printf("\tCollision count = %u\r\n", _collide_cnt(locknum)); if (retval != 0) printf("\t*** ERROR: Checking lock ***\n"); return(retval); } /*--- Printf single locks ---*/ int print_single_locks(void) { int locknum; int retval=0; int lockval; printf("\n--- Single Locks ---\n"); #ifdef _INIT_LOCKS printf("\t\t\t\tlock count\tcollide count\n"); for (locknum=1;locknum<_STREAM_LOCKS;locknum++) { if (lockval = (_check_lock(locknum) != 0)) retval++; printf("#%i / %s\t\t%u\t\t%u\t%s\n", locknum, _locknames[locknum], _lock_cnt(locknum), _collide_cnt(locknum), (lockval ? "*LOCK ERROR*" : "") ); } #else printf("\t\t\t\tlock count\tcollide count\texists?\n"); for (locknum=1;locknum<_STREAM_LOCKS;locknum++) { if (lockval = (_check_lock(locknum) != 0)) retval++; printf("#%i / %s\t\t%u\t\t%u\t\t%s\t%s\n", locknum, _locknames[locknum], _lock_cnt(locknum), _collide_cnt(locknum), (_lock_exist(locknum) ? "YES" : "NO"), (lockval ? "*LOCK ERROR*" : "") ); } #endif return(retval); } /*--- Print all stdio locks ---*/ int print_stdio_locks(void) { int i; int locknum; int retval=0; int lockval; printf("\n--- Stdio Locks ---\n"); #ifdef _INIT_LOCKS printf("stream\t\tlock count\tcollide count\n"); for (i=0;i<_NFILE;i++) { locknum = _stream_locknum(i); if (lockval = (_check_lock(locknum) != 0)) retval++; printf("%i\t\t%u\t\t%u\t%s\n", i, _lock_cnt(locknum), _collide_cnt(locknum), (lockval ? "*LOCK ERROR*" : "") ); } #else printf("stream\t\tlock count\tcollide count\texists?\n"); for (i=0;i<_NFILE;i++) { locknum = _stream_locknum(i); if (lockval = (_check_lock(locknum) != 0)) retval++; printf("%i\t\t%u\t\t%u\t\t%s\t%s\n", i, _lock_cnt(locknum), _collide_cnt(locknum), (_lock_exist(locknum) ? "YES" : "NO"), (lockval ? "*LOCK ERROR*" : "") ); } #endif return(retval); } /*--- Print all lowio locks ---*/ int print_lowio_locks(void) { int i; int locknum; int retval=0; int lockval; printf("\n--- Lowio locks ---\n"); #ifdef _INIT_LOCKS printf("fh\t\tlock count\tcollide count\n"); for (i=0;i<_NFILE;i++) { locknum = _fh_locknum(i); if (lockval = (_check_lock(locknum) != 0)) retval++; printf("%i\t\t%u\t\t%u\t%s\n", i, _lock_cnt(locknum), _collide_cnt(locknum), (lockval ? "*LOCK ERROR*" : "") ); } #else printf("fh\t\tlock count\tcollide count\texists?\n"); for (i=0;i<_NFILE;i++) { locknum = _fh_locknum(i); if (lockval = (_check_lock(locknum) != 0)) retval++; printf("%i\t\t%u\t\t%u\t\t%s\t%s\n", i, _lock_cnt(locknum), _collide_cnt(locknum), (_lock_exist(locknum) ? "YES" : "NO"), (lockval ? "*LOCK ERROR*" : "") ); } #endif return(retval); } /*--- Print all I/O locks ---*/ int print_iolocks(void) { int retval=0; retval += print_stdio_locks(); retval += print_lowio_locks(); return(retval); } /*--- Print all Locks ---*/ int print_locks(void) { int retval=0; retval += print_single_locks(); retval += print_iolocks(); return(retval); } #endif