532 lines
12 KiB
C
532 lines
12 KiB
C
/***
|
|
* 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
|