|
|
/***
* 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 <malloc.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <io.h>
#include <mtest.h>
#ifdef DEBUG
#include <mtdll.h>
#include <file2.h>
#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
|