You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
796 lines
25 KiB
796 lines
25 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996.
|
|
//
|
|
// File: ftest.cxx
|
|
//
|
|
// Contents: Code for launching the filter test
|
|
//
|
|
// Classes: none
|
|
//
|
|
// Functions: main() -------- Processes command line switches, handles
|
|
// directory filtering
|
|
// usage() ------- Displays a usage message
|
|
// LaunchTest() -- Creates a CFiltTest object, initializes it,
|
|
// and executes the test
|
|
//
|
|
// Coupling:
|
|
//
|
|
// Notes: About allocations: In this project, the keyword 'new' does
|
|
// no appear explicitely. Instead, I use a 'NEW' macro. If the
|
|
// code is compiled with the DEBUG flag, NEW will expand to
|
|
// new( __FILE__, __LINE__ ), which will invoke an overloaded
|
|
// new operator to keeps track of my allocations. Otherwise,
|
|
// NEW simply expands to 'new'.
|
|
//
|
|
// More about allocations: This program uses a newhandler to
|
|
// handle failed allocations. The newhandler loops until enough
|
|
// free memory is available, so every allocation is guaranteed
|
|
// to succeed.
|
|
//
|
|
// History: 9-16-1996 ericne Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
#include "pch.cxx"
|
|
#include <new.h>
|
|
#include <process.h>
|
|
#include "clog.hxx"
|
|
#include "workq.cxx"
|
|
#include "mydebug.hxx"
|
|
#include "filttest.hxx"
|
|
#include "cmdparse.cxx"
|
|
#include "oleinit.hxx"
|
|
|
|
// Must be compiled with the UNICODE flag
|
|
#if ! defined( UNICODE ) && ! defined( _UNICODE )
|
|
#error( "UNICODE must be defined" )
|
|
#endif
|
|
|
|
// Global work queue of size 10
|
|
CWorkQueue< WCHAR*, 10 > g_WorkQueue;
|
|
|
|
// Global parameters
|
|
int g_iDepth = 1; // Recursion depth, -1 is full
|
|
int g_cLoops = 1; // Loop counter, -1 is loop forever
|
|
BOOL g_fLogToFile = FALSE; // TRUE if log should be sent to
|
|
BOOL g_fDumpToFile = FALSE;
|
|
BOOL g_fIsLoggingEnabled = TRUE;
|
|
BOOL g_fIsDumpingEnabled = TRUE;
|
|
BOOL g_fLegitOnly = FALSE;
|
|
WCHAR *g_szIniFileName = L"ifilttst.ini";
|
|
CLog *g_pLog = NULL;
|
|
Verbosity g_verbosity = HIGH;
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: out_of_store
|
|
//
|
|
// Synopsis: A new handeler function. When new fails, this function gets
|
|
// invoked. It sleeps for a while and then returns 1, indicating
|
|
// that the allocation should be retried. In addition, it
|
|
// displays a message so the user knows what's happening.
|
|
//
|
|
// Arguments: [size] -- The size, in bytes, of the memory block to be
|
|
// allocated
|
|
//
|
|
// Returns: 1
|
|
//
|
|
// History: 10-03-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
int __cdecl out_of_store( size_t )
|
|
{
|
|
|
|
printf( "An allocation failed. Will re-try in %d milliseconds\r\n",
|
|
dwSleepTime );
|
|
|
|
Sleep( dwSleepTime );
|
|
|
|
return( 1 );
|
|
|
|
} //out_of_store
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Usage
|
|
//
|
|
// Synopsis: Displays a usage message
|
|
//
|
|
// Arguments: [pcExecName] -- name of the executable ( call with argv[0] )
|
|
//
|
|
// Returns: none
|
|
//
|
|
// History: 10-01-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Usage( const WCHAR * szExecName )
|
|
{
|
|
printf( "\r\nUSAGE:\r\n" );
|
|
printf( "%ls /i <input file>[...] [/ini <ini file>] [/l [<log file>]] [/d]"
|
|
" [/-l] [/-d] [/legit] [/stress] [/v <verbosity>] [/t <threads>]"
|
|
" [/r [<depth>]] [/c <loops>]\r\n", szExecName );
|
|
printf( "\r\n" );
|
|
printf( "\t<input file> is the file/directory/pattern to which to bind.\r\n"
|
|
"\t\tWildcards are OK. More than one input file is OK.\r\n" );
|
|
printf( "\t<ini file> is the initialization file to use. If none is\r\n"
|
|
"\t\tspecified, it defaults to ifilttst.ini.\r\n");
|
|
printf( "\t[/l] enables logging to a file. By default, the log filename\r\n"
|
|
"\t\tis the input file name with a .log extension. If you\r\n"
|
|
"\t\tspecify a log file name, all the log messages will be sent\r\n"
|
|
"\t\tto a single file.\r\n");
|
|
printf( "\t[/d] enables dumping to a file. The dump filename is the\r\n"
|
|
"\t\tinput file name with a .dmp extension.\r\n" );
|
|
printf( "\t[/-l] disables logging. This flag overrides /l.\r\n" );
|
|
printf( "\t[/-d] disables dumping. This flag overrides /d.\r\n" );
|
|
printf( "\t[/legit] forces the test to run only the Validation Test.\r\n"
|
|
"\t\tThe Consistency and Invalid Input Tests are skipped.\r\n" );
|
|
printf( "\t[/stress] forces the test to run in stress mode. This is the\r\n"
|
|
"\t\t same as specifying /-l /-d /legit /v 0 /c 0\r\n" );
|
|
printf( "\t<verbosity> is an integer representing the verbosity level\r\n"
|
|
"\t\tAcceptable values are from %d through %d, with %d being the\r\n"
|
|
"\t\tmost verbose. (default is %d)\r\n", MUTE, HIGH, HIGH, HIGH );
|
|
printf( "\t<threads> is an integer representing the number of threads\r\n"
|
|
"\t\tto launch. Only useful if filtering multiple files.\r\n"
|
|
"\t\t(default is 1)\r\n" );
|
|
printf( "\t<depth> is an integer representing the depth to recurse.\r\n"
|
|
"\t\tNo value or a value of 0 indicates full recursion. (default"
|
|
" is 1)\r\n" );
|
|
printf( "\t<loops> is an integer representing the number of times to\r\n"
|
|
"\t\tloop. A value of 0 means loop infinetly. (default is 1)\r\n");
|
|
} //Usage
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LaunchTest
|
|
//
|
|
// Synopsis: If necessary, determines what the log and dump file names should
|
|
// be, creates a CFiltTest object, initializes it, and executes the
|
|
// test.
|
|
//
|
|
// Arguments: [szInputFileName] -- Full path to input file
|
|
//
|
|
// Returns: void
|
|
//
|
|
// History: 9-26-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void LaunchTest( WCHAR * szInputFileName )
|
|
{
|
|
// This function determines the log and dump filenames from the input
|
|
// filename
|
|
WCHAR *szLogFileName = NULL;
|
|
WCHAR *szDumpFileName = NULL;
|
|
CFiltTest *pThisFiltTest = NULL;
|
|
|
|
// Try-finally block simplifies clean-up
|
|
try
|
|
{
|
|
// If we are logging to a file, create the filename
|
|
if( g_fLogToFile )
|
|
{
|
|
szLogFileName = NEW WCHAR[ MAX_PATH ];
|
|
|
|
wcscpy( szLogFileName, szInputFileName );
|
|
wcscat( szLogFileName, L".log" );
|
|
}
|
|
|
|
// If we are dumping to a file, create the filename
|
|
if( g_fDumpToFile )
|
|
{
|
|
szDumpFileName = NEW WCHAR[ MAX_PATH ];
|
|
|
|
wcscpy( szDumpFileName, szInputFileName );
|
|
wcscat( szDumpFileName, L".dmp" );
|
|
}
|
|
|
|
// Create the Filter test object
|
|
pThisFiltTest = NEW CFiltTest( g_pLog );
|
|
|
|
if( pThisFiltTest->Init( szInputFileName,
|
|
szLogFileName,
|
|
szDumpFileName,
|
|
g_szIniFileName,
|
|
g_verbosity,
|
|
g_fLegitOnly ) )
|
|
{
|
|
pThisFiltTest->ExecuteTests( );
|
|
}
|
|
} // _try
|
|
catch(...)
|
|
{
|
|
}
|
|
|
|
// _finally
|
|
{
|
|
// Clean up the heap
|
|
if( pThisFiltTest )
|
|
delete pThisFiltTest;
|
|
|
|
if( szLogFileName )
|
|
delete [] szLogFileName;
|
|
|
|
if( szDumpFileName )
|
|
delete [] szDumpFileName;
|
|
}
|
|
|
|
} //LaunchTest
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FindAllFiles
|
|
//
|
|
// Synopsis: Find all the files that meet the restriction and put them in
|
|
// the queue.
|
|
//
|
|
// Arguments: [szPath] --
|
|
// [iDepth] --
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 10-14-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void FindAllFiles( WCHAR *szPath, int iDepth )
|
|
{
|
|
HANDLE hSearch = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA FindData;
|
|
WCHAR szNewPath[ MAX_PATH + 2 ];
|
|
WCHAR szNewSearch[ MAX_PATH + 2 ];
|
|
WCHAR *szWorkItem = NULL;
|
|
|
|
// Check the depth restriction:
|
|
if( 0 == iDepth )
|
|
return;
|
|
|
|
hSearch = FindFirstFile( szPath, &FindData );
|
|
|
|
if ( 0 == *szPath )
|
|
return;
|
|
|
|
if( INVALID_HANDLE_VALUE != hSearch )
|
|
{
|
|
// Find all the files that match the pattern and put them in the queue
|
|
do
|
|
{
|
|
WCHAR *szExtension = wcsrchr( FindData.cFileName, '.' );
|
|
|
|
// If this is a directory, continue
|
|
if( FILE_ATTRIBUTE_DIRECTORY & FindData.dwFileAttributes )
|
|
continue;
|
|
|
|
// If the extension equals ".log" or ".dmp", continue
|
|
if( NULL != szExtension &&
|
|
( 0 == wcscmp( szExtension, L".log" ) ||
|
|
0 == wcscmp( szExtension, L".dmp" ) ) )
|
|
continue;
|
|
|
|
// Copy the path into NewPath
|
|
wcscpy( szNewPath, szPath );
|
|
|
|
// Remove the restriction part of the path
|
|
*( wcsrchr( szNewPath, L'\\' ) + 1 ) = L'\0';
|
|
|
|
// Append the file name of the matching file
|
|
wcscat( szNewPath, FindData.cFileName );
|
|
|
|
// Dynamically create a new work item
|
|
szWorkItem = NEW WCHAR[ wcslen( szNewPath ) + 1 ];
|
|
|
|
// Copy the full path into the new work item
|
|
wcscpy( szWorkItem, szNewPath );
|
|
|
|
// Put the work item in the queue
|
|
g_WorkQueue.AddItem( szWorkItem );
|
|
|
|
// We don't own this item anymore, so set the pointer to NULL
|
|
szWorkItem = NULL;
|
|
|
|
} while( FindNextFile( hSearch, &FindData ) );
|
|
|
|
// Close the search handle
|
|
FindClose( hSearch );
|
|
hSearch = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// Now, recurse into the subdirectories and search for the same pattern.
|
|
|
|
// Copy the origional path into a new path for the new search
|
|
|
|
wcscpy( szNewSearch, szPath );
|
|
|
|
// Remove the restriction
|
|
*( wcsrchr( szNewSearch, L'\\' ) + 1 ) = L'\0';
|
|
|
|
// Append a star
|
|
wcscat( szNewSearch, L"*" );
|
|
|
|
// Since we're looking for a wildcard, this should succeed
|
|
hSearch = FindFirstFile( szNewSearch, &FindData );
|
|
|
|
if( INVALID_HANDLE_VALUE == hSearch )
|
|
{
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Recurse into this subdirectory looking for the same pattern
|
|
|
|
if( ( FILE_ATTRIBUTE_DIRECTORY & FindData.dwFileAttributes ) &&
|
|
( 0 != wcscmp( FindData.cFileName, L"." ) ) &&
|
|
( 0 != wcscmp( FindData.cFileName, L".." ) ) )
|
|
{
|
|
// Copy the search string into NewPath
|
|
wcscpy( szNewPath, szNewSearch );
|
|
|
|
// Remove the "*" at the end
|
|
*( wcsrchr( szNewPath, L'\\' ) + 1 ) = L'\0';
|
|
|
|
// Append the directory name found
|
|
wcscat( szNewPath, FindData.cFileName );
|
|
|
|
// Append a slash
|
|
wcscat( szNewPath, L"\\" );
|
|
|
|
// Finally, append the origional search restriction
|
|
wcscat( szNewPath, wcsrchr( szPath, L'\\' ) + 1 );
|
|
|
|
// Recurse
|
|
FindAllFiles( szNewPath, iDepth - 1 );
|
|
}
|
|
} while( FindNextFile( hSearch, &FindData ) );
|
|
|
|
FindClose( hSearch );
|
|
|
|
hSearch = INVALID_HANDLE_VALUE;
|
|
|
|
} //FindAllFiles
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Producer
|
|
//
|
|
// Synopsis: A thread which collects all the input file names and puts them
|
|
// in the work queue
|
|
//
|
|
// Arguments: [lpvThreadParam] -- The input file name
|
|
//
|
|
// Returns: 0
|
|
//
|
|
// History: 10-01-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI Producer( PVOID pvThreadParam )
|
|
{
|
|
WCHAR szFullPath[ MAX_PATH + 2 ];
|
|
DWORD dwAttrib = 0;
|
|
int cLoops = g_cLoops;
|
|
|
|
GetFullPathName( (WCHAR*)pvThreadParam, MAX_PATH, szFullPath, NULL );
|
|
|
|
// If the input is recognized as a directory, only recurse into this
|
|
// directory. Otherwise, recurse into all subdirectories and try to
|
|
// match the pattern
|
|
|
|
dwAttrib = GetFileAttributes( szFullPath );
|
|
|
|
if( ( 0xFFFFFFFF != dwAttrib ) && ( FILE_ATTRIBUTE_DIRECTORY & dwAttrib ) )
|
|
{
|
|
// It's a directory. If the last character is a '\\', add "*"
|
|
// Otherwise, add "\\*"
|
|
if( L'\\' == szFullPath[ wcslen( szFullPath ) - 1 ] )
|
|
{
|
|
wcscat( szFullPath, L"*" );
|
|
}
|
|
else
|
|
{
|
|
wcscat( szFullPath, L"\\*" );
|
|
}
|
|
}
|
|
|
|
while( cLoops-- )
|
|
{
|
|
FindAllFiles( szFullPath, g_iDepth );
|
|
}
|
|
|
|
return( 0 );
|
|
|
|
} //Producer
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Consumer
|
|
//
|
|
// Synopsis: Pulls stuff out of the work queue and calls LaunchTest on the
|
|
// file name. It is also responsible for deleting the file
|
|
// names it pulls out of the list
|
|
//
|
|
// Arguments: [lpvThreadParam] -- Thread number
|
|
//
|
|
// Returns: 0
|
|
//
|
|
// History: 10-01-1996 ericne Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD WINAPI Consumer( PVOID pvThreadParam )
|
|
{
|
|
WCHAR *szInputFileName = NULL;
|
|
COleInitialize OleIsInitialized; // This object ensures OLE is initialized
|
|
// for this thread
|
|
|
|
while( g_WorkQueue.GetItem( szInputFileName ) )
|
|
{
|
|
// Display which thread is filtering which document
|
|
if( NORMAL <= g_verbosity )
|
|
{
|
|
wprintf(L"Thread %d is filtering %s\r\n",
|
|
(UINT_PTR)pvThreadParam, szInputFileName );
|
|
}
|
|
|
|
LaunchTest( szInputFileName );
|
|
delete [] szInputFileName;
|
|
}
|
|
|
|
return( 0 );
|
|
|
|
} //Consumer
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: wmain
|
|
//
|
|
// Synopsis: Processes command switches, Launched producer and consumer
|
|
// threads
|
|
//
|
|
// Arguments: [argc] -- The number of command line parameters
|
|
// [argv] -- The value of the command line parameters
|
|
//
|
|
// Returns: 0
|
|
//
|
|
// History: 10-01-1996 ericne Created
|
|
//
|
|
// Notes: extern "C" to satisfy the linker
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
extern "C" int __cdecl wmain( int argc, WCHAR **argv )
|
|
{
|
|
int iLoop = 0;
|
|
int iCount = 0;
|
|
int iNbrConsumers = 1;
|
|
int iNbrProducers = 1;
|
|
WCHAR **ppwcParams = NULL;
|
|
WCHAR *pwcBadParam = NULL;
|
|
_PNH pfOldNewHandler = NULL;
|
|
LPCWSTR *rgszInputFileName;
|
|
TCHAR szLogFileName[ MAX_PATH ];
|
|
DWORD dwThreadID = 0;
|
|
HANDLE *hConsumerThreads = NULL;
|
|
HANDLE *hProducerThreads = NULL;
|
|
CCmdLineParserW CmdLineParser( argc, argv );
|
|
|
|
|
|
try
|
|
{
|
|
// Set the new handeler routine
|
|
pfOldNewHandler = _set_new_handler( out_of_store );
|
|
|
|
// Check the command line parameters:
|
|
if( CmdLineParser.IsFlagExist( L"?" ) )
|
|
{
|
|
Usage( argv[0] );
|
|
exit( 0 );
|
|
}
|
|
|
|
// Find /d flag
|
|
if( CmdLineParser.IsFlagExist( L"d" ) )
|
|
g_fDumpToFile = TRUE;
|
|
|
|
// Find /-l flag
|
|
if( CmdLineParser.IsFlagExist( L"-l" ) )
|
|
g_fIsLoggingEnabled = FALSE;
|
|
|
|
// Find /-d flag
|
|
if( CmdLineParser.IsFlagExist( L"-d" ) )
|
|
g_fIsDumpingEnabled = FALSE;
|
|
|
|
// Find the legit flag
|
|
if( CmdLineParser.IsFlagExist( L"legit" ) )
|
|
g_fLegitOnly = TRUE;
|
|
|
|
// Find /i flag
|
|
if( CmdLineParser.EnumerateFlag( L"i", ppwcParams, iCount ) )
|
|
{
|
|
if( 1 > iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: 1 or more input files must be specified\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
iNbrProducers = iCount;
|
|
|
|
// Create array of file patterns
|
|
rgszInputFileName = NEW LPCWSTR[ iCount ];
|
|
|
|
for( int iParam=0; iParam < iCount; iParam++ )
|
|
{
|
|
rgszInputFileName[ iParam ] = ppwcParams[ iParam ];
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: An input file must be specified\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
// find the /ini flag
|
|
if( CmdLineParser.EnumerateFlag( L"ini", ppwcParams, iCount ) )
|
|
{
|
|
if( 1 != iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: You must specify exactly one"
|
|
" initialization file.\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
g_szIniFileName = ppwcParams[0];
|
|
}
|
|
|
|
// Find the /v flag
|
|
if( CmdLineParser.EnumerateFlag( L"v", ppwcParams, iCount ) )
|
|
{
|
|
if( 1 != iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: You must specify exactly 1 verbosity\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
// Get the new verbosity:
|
|
g_verbosity = (Verbosity) _wtoi( ppwcParams[0] );
|
|
|
|
if( MUTE > g_verbosity || HIGH < g_verbosity )
|
|
{
|
|
printf( "ERROR: The verbosity must be between %d and %d"
|
|
" inclusive.\r\n", MUTE, HIGH );
|
|
exit( -1 );
|
|
}
|
|
}
|
|
|
|
// Find the /l flag
|
|
if( CmdLineParser.EnumerateFlag( L"l", ppwcParams, iCount ) )
|
|
{
|
|
g_fLogToFile = TRUE;
|
|
|
|
if( 1 < iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: You may only specity one log file\r\n" );
|
|
exit( -1 );
|
|
}
|
|
else if( 1 == iCount )
|
|
{
|
|
// Create a common log file object
|
|
g_pLog = NEW CLog;
|
|
|
|
// Should succeed because of the new handler
|
|
_ASSERT( NULL != g_pLog );
|
|
|
|
// Convert to tchar:
|
|
_stprintf( szLogFileName, _T("%ls"), ppwcParams[0] );
|
|
|
|
// Initialize the log
|
|
if( ! g_pLog->InitLog( szLogFileName ) )
|
|
{
|
|
printf( "ERROR: Could not initialize log file %s\r\n",
|
|
szLogFileName );
|
|
exit( -1 );
|
|
}
|
|
|
|
// Set the log threshold
|
|
g_pLog->SetThreshold( VerbosityToLogStyle( g_verbosity ) );
|
|
|
|
}
|
|
}
|
|
|
|
// Find the /t flag
|
|
if( CmdLineParser.EnumerateFlag( L"t", ppwcParams, iCount ) )
|
|
{
|
|
if( 1 != iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: You must specify only 1 thread count\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
// Get the new thread count
|
|
iNbrConsumers = _wtoi( ppwcParams[0] );
|
|
|
|
if( 1 > iNbrConsumers || MAXIMUM_WAIT_OBJECTS < iNbrConsumers )
|
|
{
|
|
printf( "The thread count must be between 1 and %d inclusive\r\n",
|
|
MAXIMUM_WAIT_OBJECTS );
|
|
exit( -1 );
|
|
}
|
|
|
|
}
|
|
|
|
// Find the /r flag
|
|
if( CmdLineParser.EnumerateFlag( L"r", ppwcParams, iCount ) )
|
|
{
|
|
// If no depth is specified, assume full recursion
|
|
if( 0 == iCount )
|
|
{
|
|
g_iDepth = -1;
|
|
}
|
|
else
|
|
{
|
|
g_iDepth = _wtoi( ppwcParams[0] );
|
|
|
|
// Special case, if the recurse depth is 0, perform full recursion
|
|
if( 0 == g_iDepth )
|
|
g_iDepth = -1;
|
|
}
|
|
}
|
|
|
|
// Find the /c flag
|
|
if( CmdLineParser.EnumerateFlag( L"c", ppwcParams, iCount ) )
|
|
{
|
|
if( 1 != iCount )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: You may only specify 1 loop count\r\n" );
|
|
exit( -1 );
|
|
}
|
|
|
|
// Get the new thread count
|
|
g_cLoops = _wtoi( ppwcParams[0] );
|
|
|
|
// Special case: if cLoops == 0, loop forever
|
|
if( 0 == g_cLoops )
|
|
g_cLoops = -1;
|
|
}
|
|
|
|
// This flag configures for a stress test
|
|
if( CmdLineParser.IsFlagExist( L"stress" ) )
|
|
{
|
|
g_fIsLoggingEnabled = FALSE;
|
|
g_fIsDumpingEnabled = FALSE;
|
|
g_fLegitOnly = TRUE;
|
|
g_verbosity = MUTE;
|
|
g_cLoops = -1;
|
|
}
|
|
|
|
if( CmdLineParser.GetNextFlag( pwcBadParam ) )
|
|
{
|
|
Usage( argv[0] );
|
|
printf( "ERROR: Unknown command line switch : %ls\r\n", pwcBadParam );
|
|
exit( -1 );
|
|
}
|
|
|
|
// Done processing switches
|
|
|
|
// Allocate memory for the thread handles
|
|
hProducerThreads = NEW HANDLE[ iNbrProducers ];
|
|
hConsumerThreads = NEW HANDLE[ iNbrConsumers ];
|
|
|
|
// Launch the producer threads
|
|
for( iLoop = 0; iLoop < iNbrProducers; iLoop++ )
|
|
{
|
|
while( NULL == ( hProducerThreads[ iLoop ] = CreateThread(
|
|
NULL, 0, &Producer, (void*)rgszInputFileName[ iLoop ],
|
|
0, &dwThreadID ) ) )
|
|
{
|
|
Sleep( dwSleepTime );
|
|
}
|
|
}
|
|
|
|
// Launch the consumer threads
|
|
for( iLoop = 0; iLoop < iNbrConsumers; iLoop++ )
|
|
{
|
|
while( NULL == ( hConsumerThreads[ iLoop ] = CreateThread(
|
|
NULL, 0, &Consumer, (void*)IntToPtr(iLoop), 0, &dwThreadID ) ) )
|
|
{
|
|
Sleep( dwSleepTime );
|
|
}
|
|
}
|
|
|
|
// Wait for all the producers to finish
|
|
WaitForMultipleObjects( (DWORD) iNbrProducers,
|
|
hProducerThreads,
|
|
TRUE,
|
|
INFINITE );
|
|
|
|
// Signal the Consumer threads to finish.
|
|
g_WorkQueue.Done();
|
|
|
|
// Wait for the consumer threads to finish
|
|
WaitForMultipleObjects( (DWORD) iNbrConsumers,
|
|
hConsumerThreads,
|
|
TRUE,
|
|
INFINITE );
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
|
|
{
|
|
// Close all the handles
|
|
if( NULL != hProducerThreads )
|
|
{
|
|
for( iLoop=0; iLoop < iNbrProducers; iLoop++ )
|
|
{
|
|
if( NULL != hProducerThreads[ iLoop ] )
|
|
{
|
|
(void)CloseHandle( hProducerThreads[ iLoop ] );
|
|
hProducerThreads[ iLoop ] = NULL;
|
|
}
|
|
}
|
|
delete[] hProducerThreads;
|
|
hProducerThreads = NULL;
|
|
}
|
|
|
|
if( NULL != hConsumerThreads )
|
|
{
|
|
for( iLoop = 0; iLoop < iNbrConsumers; iLoop++ )
|
|
{
|
|
if( NULL != hConsumerThreads[ iLoop ] )
|
|
{
|
|
(void)CloseHandle( hConsumerThreads[ iLoop ] );
|
|
hConsumerThreads[ iLoop ] = NULL;
|
|
}
|
|
}
|
|
delete[] hConsumerThreads;
|
|
hConsumerThreads = NULL;
|
|
}
|
|
|
|
if( NULL != rgszInputFileName )
|
|
{
|
|
delete[] rgszInputFileName;
|
|
rgszInputFileName = NULL;
|
|
}
|
|
|
|
// If there is a shared log, report stats and quit
|
|
if( NULL != g_pLog )
|
|
{
|
|
g_pLog->ReportStats( );
|
|
delete g_pLog;
|
|
g_pLog = NULL;
|
|
}
|
|
|
|
// Restore the old new handler
|
|
_set_new_handler( pfOldNewHandler );
|
|
|
|
// Shut down CI to prevent memory leaks
|
|
CIShutdown();
|
|
}
|
|
|
|
return( 0 );
|
|
} //main
|
|
|