windows-nt/Source/XPSP1/NT/base/crts/crtw32/misc/mtest.c

532 lines
12 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/***
* 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