Leaked source code of windows server 2003
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

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996.
  5. //
  6. // File: ftest.cxx
  7. //
  8. // Contents: Code for launching the filter test
  9. //
  10. // Classes: none
  11. //
  12. // Functions: main() -------- Processes command line switches, handles
  13. // directory filtering
  14. // usage() ------- Displays a usage message
  15. // LaunchTest() -- Creates a CFiltTest object, initializes it,
  16. // and executes the test
  17. //
  18. // Coupling:
  19. //
  20. // Notes: About allocations: In this project, the keyword 'new' does
  21. // no appear explicitely. Instead, I use a 'NEW' macro. If the
  22. // code is compiled with the DEBUG flag, NEW will expand to
  23. // new( __FILE__, __LINE__ ), which will invoke an overloaded
  24. // new operator to keeps track of my allocations. Otherwise,
  25. // NEW simply expands to 'new'.
  26. //
  27. // More about allocations: This program uses a newhandler to
  28. // handle failed allocations. The newhandler loops until enough
  29. // free memory is available, so every allocation is guaranteed
  30. // to succeed.
  31. //
  32. // History: 9-16-1996 ericne Created
  33. //
  34. //----------------------------------------------------------------------------
  35. #include "pch.cxx"
  36. #include <new.h>
  37. #include <process.h>
  38. #include "clog.hxx"
  39. #include "workq.cxx"
  40. #include "mydebug.hxx"
  41. #include "filttest.hxx"
  42. #include "cmdparse.cxx"
  43. #include "oleinit.hxx"
  44. // Must be compiled with the UNICODE flag
  45. #if ! defined( UNICODE ) && ! defined( _UNICODE )
  46. #error( "UNICODE must be defined" )
  47. #endif
  48. // Global work queue of size 10
  49. CWorkQueue< WCHAR*, 10 > g_WorkQueue;
  50. // Global parameters
  51. int g_iDepth = 1; // Recursion depth, -1 is full
  52. int g_cLoops = 1; // Loop counter, -1 is loop forever
  53. BOOL g_fLogToFile = FALSE; // TRUE if log should be sent to
  54. BOOL g_fDumpToFile = FALSE;
  55. BOOL g_fIsLoggingEnabled = TRUE;
  56. BOOL g_fIsDumpingEnabled = TRUE;
  57. BOOL g_fLegitOnly = FALSE;
  58. WCHAR *g_szIniFileName = L"ifilttst.ini";
  59. CLog *g_pLog = NULL;
  60. Verbosity g_verbosity = HIGH;
  61. //+---------------------------------------------------------------------------
  62. //
  63. // Function: out_of_store
  64. //
  65. // Synopsis: A new handeler function. When new fails, this function gets
  66. // invoked. It sleeps for a while and then returns 1, indicating
  67. // that the allocation should be retried. In addition, it
  68. // displays a message so the user knows what's happening.
  69. //
  70. // Arguments: [size] -- The size, in bytes, of the memory block to be
  71. // allocated
  72. //
  73. // Returns: 1
  74. //
  75. // History: 10-03-1996 ericne Created
  76. //
  77. // Notes:
  78. //
  79. //----------------------------------------------------------------------------
  80. int __cdecl out_of_store( size_t )
  81. {
  82. printf( "An allocation failed. Will re-try in %d milliseconds\r\n",
  83. dwSleepTime );
  84. Sleep( dwSleepTime );
  85. return( 1 );
  86. } //out_of_store
  87. //+---------------------------------------------------------------------------
  88. //
  89. // Function: Usage
  90. //
  91. // Synopsis: Displays a usage message
  92. //
  93. // Arguments: [pcExecName] -- name of the executable ( call with argv[0] )
  94. //
  95. // Returns: none
  96. //
  97. // History: 10-01-1996 ericne Created
  98. //
  99. // Notes:
  100. //
  101. //----------------------------------------------------------------------------
  102. void Usage( const WCHAR * szExecName )
  103. {
  104. printf( "\r\nUSAGE:\r\n" );
  105. printf( "%ls /i <input file>[...] [/ini <ini file>] [/l [<log file>]] [/d]"
  106. " [/-l] [/-d] [/legit] [/stress] [/v <verbosity>] [/t <threads>]"
  107. " [/r [<depth>]] [/c <loops>]\r\n", szExecName );
  108. printf( "\r\n" );
  109. printf( "\t<input file> is the file/directory/pattern to which to bind.\r\n"
  110. "\t\tWildcards are OK. More than one input file is OK.\r\n" );
  111. printf( "\t<ini file> is the initialization file to use. If none is\r\n"
  112. "\t\tspecified, it defaults to ifilttst.ini.\r\n");
  113. printf( "\t[/l] enables logging to a file. By default, the log filename\r\n"
  114. "\t\tis the input file name with a .log extension. If you\r\n"
  115. "\t\tspecify a log file name, all the log messages will be sent\r\n"
  116. "\t\tto a single file.\r\n");
  117. printf( "\t[/d] enables dumping to a file. The dump filename is the\r\n"
  118. "\t\tinput file name with a .dmp extension.\r\n" );
  119. printf( "\t[/-l] disables logging. This flag overrides /l.\r\n" );
  120. printf( "\t[/-d] disables dumping. This flag overrides /d.\r\n" );
  121. printf( "\t[/legit] forces the test to run only the Validation Test.\r\n"
  122. "\t\tThe Consistency and Invalid Input Tests are skipped.\r\n" );
  123. printf( "\t[/stress] forces the test to run in stress mode. This is the\r\n"
  124. "\t\t same as specifying /-l /-d /legit /v 0 /c 0\r\n" );
  125. printf( "\t<verbosity> is an integer representing the verbosity level\r\n"
  126. "\t\tAcceptable values are from %d through %d, with %d being the\r\n"
  127. "\t\tmost verbose. (default is %d)\r\n", MUTE, HIGH, HIGH, HIGH );
  128. printf( "\t<threads> is an integer representing the number of threads\r\n"
  129. "\t\tto launch. Only useful if filtering multiple files.\r\n"
  130. "\t\t(default is 1)\r\n" );
  131. printf( "\t<depth> is an integer representing the depth to recurse.\r\n"
  132. "\t\tNo value or a value of 0 indicates full recursion. (default"
  133. " is 1)\r\n" );
  134. printf( "\t<loops> is an integer representing the number of times to\r\n"
  135. "\t\tloop. A value of 0 means loop infinetly. (default is 1)\r\n");
  136. } //Usage
  137. //+---------------------------------------------------------------------------
  138. //
  139. // Function: LaunchTest
  140. //
  141. // Synopsis: If necessary, determines what the log and dump file names should
  142. // be, creates a CFiltTest object, initializes it, and executes the
  143. // test.
  144. //
  145. // Arguments: [szInputFileName] -- Full path to input file
  146. //
  147. // Returns: void
  148. //
  149. // History: 9-26-1996 ericne Created
  150. //
  151. // Notes:
  152. //
  153. //----------------------------------------------------------------------------
  154. void LaunchTest( WCHAR * szInputFileName )
  155. {
  156. // This function determines the log and dump filenames from the input
  157. // filename
  158. WCHAR *szLogFileName = NULL;
  159. WCHAR *szDumpFileName = NULL;
  160. CFiltTest *pThisFiltTest = NULL;
  161. // Try-finally block simplifies clean-up
  162. try
  163. {
  164. // If we are logging to a file, create the filename
  165. if( g_fLogToFile )
  166. {
  167. szLogFileName = NEW WCHAR[ MAX_PATH ];
  168. wcscpy( szLogFileName, szInputFileName );
  169. wcscat( szLogFileName, L".log" );
  170. }
  171. // If we are dumping to a file, create the filename
  172. if( g_fDumpToFile )
  173. {
  174. szDumpFileName = NEW WCHAR[ MAX_PATH ];
  175. wcscpy( szDumpFileName, szInputFileName );
  176. wcscat( szDumpFileName, L".dmp" );
  177. }
  178. // Create the Filter test object
  179. pThisFiltTest = NEW CFiltTest( g_pLog );
  180. if( pThisFiltTest->Init( szInputFileName,
  181. szLogFileName,
  182. szDumpFileName,
  183. g_szIniFileName,
  184. g_verbosity,
  185. g_fLegitOnly ) )
  186. {
  187. pThisFiltTest->ExecuteTests( );
  188. }
  189. } // _try
  190. catch(...)
  191. {
  192. }
  193. // _finally
  194. {
  195. // Clean up the heap
  196. if( pThisFiltTest )
  197. delete pThisFiltTest;
  198. if( szLogFileName )
  199. delete [] szLogFileName;
  200. if( szDumpFileName )
  201. delete [] szDumpFileName;
  202. }
  203. } //LaunchTest
  204. //+---------------------------------------------------------------------------
  205. //
  206. // Function: FindAllFiles
  207. //
  208. // Synopsis: Find all the files that meet the restriction and put them in
  209. // the queue.
  210. //
  211. // Arguments: [szPath] --
  212. // [iDepth] --
  213. //
  214. // Returns:
  215. //
  216. // History: 10-14-1996 ericne Created
  217. //
  218. // Notes:
  219. //
  220. //----------------------------------------------------------------------------
  221. void FindAllFiles( WCHAR *szPath, int iDepth )
  222. {
  223. HANDLE hSearch = INVALID_HANDLE_VALUE;
  224. WIN32_FIND_DATA FindData;
  225. WCHAR szNewPath[ MAX_PATH + 2 ];
  226. WCHAR szNewSearch[ MAX_PATH + 2 ];
  227. WCHAR *szWorkItem = NULL;
  228. // Check the depth restriction:
  229. if( 0 == iDepth )
  230. return;
  231. hSearch = FindFirstFile( szPath, &FindData );
  232. if ( 0 == *szPath )
  233. return;
  234. if( INVALID_HANDLE_VALUE != hSearch )
  235. {
  236. // Find all the files that match the pattern and put them in the queue
  237. do
  238. {
  239. WCHAR *szExtension = wcsrchr( FindData.cFileName, '.' );
  240. // If this is a directory, continue
  241. if( FILE_ATTRIBUTE_DIRECTORY & FindData.dwFileAttributes )
  242. continue;
  243. // If the extension equals ".log" or ".dmp", continue
  244. if( NULL != szExtension &&
  245. ( 0 == wcscmp( szExtension, L".log" ) ||
  246. 0 == wcscmp( szExtension, L".dmp" ) ) )
  247. continue;
  248. // Copy the path into NewPath
  249. wcscpy( szNewPath, szPath );
  250. // Remove the restriction part of the path
  251. *( wcsrchr( szNewPath, L'\\' ) + 1 ) = L'\0';
  252. // Append the file name of the matching file
  253. wcscat( szNewPath, FindData.cFileName );
  254. // Dynamically create a new work item
  255. szWorkItem = NEW WCHAR[ wcslen( szNewPath ) + 1 ];
  256. // Copy the full path into the new work item
  257. wcscpy( szWorkItem, szNewPath );
  258. // Put the work item in the queue
  259. g_WorkQueue.AddItem( szWorkItem );
  260. // We don't own this item anymore, so set the pointer to NULL
  261. szWorkItem = NULL;
  262. } while( FindNextFile( hSearch, &FindData ) );
  263. // Close the search handle
  264. FindClose( hSearch );
  265. hSearch = INVALID_HANDLE_VALUE;
  266. }
  267. // Now, recurse into the subdirectories and search for the same pattern.
  268. // Copy the origional path into a new path for the new search
  269. wcscpy( szNewSearch, szPath );
  270. // Remove the restriction
  271. *( wcsrchr( szNewSearch, L'\\' ) + 1 ) = L'\0';
  272. // Append a star
  273. wcscat( szNewSearch, L"*" );
  274. // Since we're looking for a wildcard, this should succeed
  275. hSearch = FindFirstFile( szNewSearch, &FindData );
  276. if( INVALID_HANDLE_VALUE == hSearch )
  277. {
  278. return;
  279. }
  280. do
  281. {
  282. // Recurse into this subdirectory looking for the same pattern
  283. if( ( FILE_ATTRIBUTE_DIRECTORY & FindData.dwFileAttributes ) &&
  284. ( 0 != wcscmp( FindData.cFileName, L"." ) ) &&
  285. ( 0 != wcscmp( FindData.cFileName, L".." ) ) )
  286. {
  287. // Copy the search string into NewPath
  288. wcscpy( szNewPath, szNewSearch );
  289. // Remove the "*" at the end
  290. *( wcsrchr( szNewPath, L'\\' ) + 1 ) = L'\0';
  291. // Append the directory name found
  292. wcscat( szNewPath, FindData.cFileName );
  293. // Append a slash
  294. wcscat( szNewPath, L"\\" );
  295. // Finally, append the origional search restriction
  296. wcscat( szNewPath, wcsrchr( szPath, L'\\' ) + 1 );
  297. // Recurse
  298. FindAllFiles( szNewPath, iDepth - 1 );
  299. }
  300. } while( FindNextFile( hSearch, &FindData ) );
  301. FindClose( hSearch );
  302. hSearch = INVALID_HANDLE_VALUE;
  303. } //FindAllFiles
  304. //+---------------------------------------------------------------------------
  305. //
  306. // Function: Producer
  307. //
  308. // Synopsis: A thread which collects all the input file names and puts them
  309. // in the work queue
  310. //
  311. // Arguments: [lpvThreadParam] -- The input file name
  312. //
  313. // Returns: 0
  314. //
  315. // History: 10-01-1996 ericne Created
  316. //
  317. // Notes:
  318. //
  319. //----------------------------------------------------------------------------
  320. DWORD WINAPI Producer( PVOID pvThreadParam )
  321. {
  322. WCHAR szFullPath[ MAX_PATH + 2 ];
  323. DWORD dwAttrib = 0;
  324. int cLoops = g_cLoops;
  325. GetFullPathName( (WCHAR*)pvThreadParam, MAX_PATH, szFullPath, NULL );
  326. // If the input is recognized as a directory, only recurse into this
  327. // directory. Otherwise, recurse into all subdirectories and try to
  328. // match the pattern
  329. dwAttrib = GetFileAttributes( szFullPath );
  330. if( ( 0xFFFFFFFF != dwAttrib ) && ( FILE_ATTRIBUTE_DIRECTORY & dwAttrib ) )
  331. {
  332. // It's a directory. If the last character is a '\\', add "*"
  333. // Otherwise, add "\\*"
  334. if( L'\\' == szFullPath[ wcslen( szFullPath ) - 1 ] )
  335. {
  336. wcscat( szFullPath, L"*" );
  337. }
  338. else
  339. {
  340. wcscat( szFullPath, L"\\*" );
  341. }
  342. }
  343. while( cLoops-- )
  344. {
  345. FindAllFiles( szFullPath, g_iDepth );
  346. }
  347. return( 0 );
  348. } //Producer
  349. //+---------------------------------------------------------------------------
  350. //
  351. // Function: Consumer
  352. //
  353. // Synopsis: Pulls stuff out of the work queue and calls LaunchTest on the
  354. // file name. It is also responsible for deleting the file
  355. // names it pulls out of the list
  356. //
  357. // Arguments: [lpvThreadParam] -- Thread number
  358. //
  359. // Returns: 0
  360. //
  361. // History: 10-01-1996 ericne Created
  362. //
  363. // Notes:
  364. //
  365. //----------------------------------------------------------------------------
  366. DWORD WINAPI Consumer( PVOID pvThreadParam )
  367. {
  368. WCHAR *szInputFileName = NULL;
  369. COleInitialize OleIsInitialized; // This object ensures OLE is initialized
  370. // for this thread
  371. while( g_WorkQueue.GetItem( szInputFileName ) )
  372. {
  373. // Display which thread is filtering which document
  374. if( NORMAL <= g_verbosity )
  375. {
  376. wprintf(L"Thread %d is filtering %s\r\n",
  377. (UINT_PTR)pvThreadParam, szInputFileName );
  378. }
  379. LaunchTest( szInputFileName );
  380. delete [] szInputFileName;
  381. }
  382. return( 0 );
  383. } //Consumer
  384. //+---------------------------------------------------------------------------
  385. //
  386. // Function: wmain
  387. //
  388. // Synopsis: Processes command switches, Launched producer and consumer
  389. // threads
  390. //
  391. // Arguments: [argc] -- The number of command line parameters
  392. // [argv] -- The value of the command line parameters
  393. //
  394. // Returns: 0
  395. //
  396. // History: 10-01-1996 ericne Created
  397. //
  398. // Notes: extern "C" to satisfy the linker
  399. //
  400. //----------------------------------------------------------------------------
  401. extern "C" int __cdecl wmain( int argc, WCHAR **argv )
  402. {
  403. int iLoop = 0;
  404. int iCount = 0;
  405. int iNbrConsumers = 1;
  406. int iNbrProducers = 1;
  407. WCHAR **ppwcParams = NULL;
  408. WCHAR *pwcBadParam = NULL;
  409. _PNH pfOldNewHandler = NULL;
  410. LPCWSTR *rgszInputFileName;
  411. TCHAR szLogFileName[ MAX_PATH ];
  412. DWORD dwThreadID = 0;
  413. HANDLE *hConsumerThreads = NULL;
  414. HANDLE *hProducerThreads = NULL;
  415. CCmdLineParserW CmdLineParser( argc, argv );
  416. try
  417. {
  418. // Set the new handeler routine
  419. pfOldNewHandler = _set_new_handler( out_of_store );
  420. // Check the command line parameters:
  421. if( CmdLineParser.IsFlagExist( L"?" ) )
  422. {
  423. Usage( argv[0] );
  424. exit( 0 );
  425. }
  426. // Find /d flag
  427. if( CmdLineParser.IsFlagExist( L"d" ) )
  428. g_fDumpToFile = TRUE;
  429. // Find /-l flag
  430. if( CmdLineParser.IsFlagExist( L"-l" ) )
  431. g_fIsLoggingEnabled = FALSE;
  432. // Find /-d flag
  433. if( CmdLineParser.IsFlagExist( L"-d" ) )
  434. g_fIsDumpingEnabled = FALSE;
  435. // Find the legit flag
  436. if( CmdLineParser.IsFlagExist( L"legit" ) )
  437. g_fLegitOnly = TRUE;
  438. // Find /i flag
  439. if( CmdLineParser.EnumerateFlag( L"i", ppwcParams, iCount ) )
  440. {
  441. if( 1 > iCount )
  442. {
  443. Usage( argv[0] );
  444. printf( "ERROR: 1 or more input files must be specified\r\n" );
  445. exit( -1 );
  446. }
  447. iNbrProducers = iCount;
  448. // Create array of file patterns
  449. rgszInputFileName = NEW LPCWSTR[ iCount ];
  450. for( int iParam=0; iParam < iCount; iParam++ )
  451. {
  452. rgszInputFileName[ iParam ] = ppwcParams[ iParam ];
  453. }
  454. }
  455. else
  456. {
  457. Usage( argv[0] );
  458. printf( "ERROR: An input file must be specified\r\n" );
  459. exit( -1 );
  460. }
  461. // find the /ini flag
  462. if( CmdLineParser.EnumerateFlag( L"ini", ppwcParams, iCount ) )
  463. {
  464. if( 1 != iCount )
  465. {
  466. Usage( argv[0] );
  467. printf( "ERROR: You must specify exactly one"
  468. " initialization file.\r\n" );
  469. exit( -1 );
  470. }
  471. g_szIniFileName = ppwcParams[0];
  472. }
  473. // Find the /v flag
  474. if( CmdLineParser.EnumerateFlag( L"v", ppwcParams, iCount ) )
  475. {
  476. if( 1 != iCount )
  477. {
  478. Usage( argv[0] );
  479. printf( "ERROR: You must specify exactly 1 verbosity\r\n" );
  480. exit( -1 );
  481. }
  482. // Get the new verbosity:
  483. g_verbosity = (Verbosity) _wtoi( ppwcParams[0] );
  484. if( MUTE > g_verbosity || HIGH < g_verbosity )
  485. {
  486. printf( "ERROR: The verbosity must be between %d and %d"
  487. " inclusive.\r\n", MUTE, HIGH );
  488. exit( -1 );
  489. }
  490. }
  491. // Find the /l flag
  492. if( CmdLineParser.EnumerateFlag( L"l", ppwcParams, iCount ) )
  493. {
  494. g_fLogToFile = TRUE;
  495. if( 1 < iCount )
  496. {
  497. Usage( argv[0] );
  498. printf( "ERROR: You may only specity one log file\r\n" );
  499. exit( -1 );
  500. }
  501. else if( 1 == iCount )
  502. {
  503. // Create a common log file object
  504. g_pLog = NEW CLog;
  505. // Should succeed because of the new handler
  506. _ASSERT( NULL != g_pLog );
  507. // Convert to tchar:
  508. _stprintf( szLogFileName, _T("%ls"), ppwcParams[0] );
  509. // Initialize the log
  510. if( ! g_pLog->InitLog( szLogFileName ) )
  511. {
  512. printf( "ERROR: Could not initialize log file %s\r\n",
  513. szLogFileName );
  514. exit( -1 );
  515. }
  516. // Set the log threshold
  517. g_pLog->SetThreshold( VerbosityToLogStyle( g_verbosity ) );
  518. }
  519. }
  520. // Find the /t flag
  521. if( CmdLineParser.EnumerateFlag( L"t", ppwcParams, iCount ) )
  522. {
  523. if( 1 != iCount )
  524. {
  525. Usage( argv[0] );
  526. printf( "ERROR: You must specify only 1 thread count\r\n" );
  527. exit( -1 );
  528. }
  529. // Get the new thread count
  530. iNbrConsumers = _wtoi( ppwcParams[0] );
  531. if( 1 > iNbrConsumers || MAXIMUM_WAIT_OBJECTS < iNbrConsumers )
  532. {
  533. printf( "The thread count must be between 1 and %d inclusive\r\n",
  534. MAXIMUM_WAIT_OBJECTS );
  535. exit( -1 );
  536. }
  537. }
  538. // Find the /r flag
  539. if( CmdLineParser.EnumerateFlag( L"r", ppwcParams, iCount ) )
  540. {
  541. // If no depth is specified, assume full recursion
  542. if( 0 == iCount )
  543. {
  544. g_iDepth = -1;
  545. }
  546. else
  547. {
  548. g_iDepth = _wtoi( ppwcParams[0] );
  549. // Special case, if the recurse depth is 0, perform full recursion
  550. if( 0 == g_iDepth )
  551. g_iDepth = -1;
  552. }
  553. }
  554. // Find the /c flag
  555. if( CmdLineParser.EnumerateFlag( L"c", ppwcParams, iCount ) )
  556. {
  557. if( 1 != iCount )
  558. {
  559. Usage( argv[0] );
  560. printf( "ERROR: You may only specify 1 loop count\r\n" );
  561. exit( -1 );
  562. }
  563. // Get the new thread count
  564. g_cLoops = _wtoi( ppwcParams[0] );
  565. // Special case: if cLoops == 0, loop forever
  566. if( 0 == g_cLoops )
  567. g_cLoops = -1;
  568. }
  569. // This flag configures for a stress test
  570. if( CmdLineParser.IsFlagExist( L"stress" ) )
  571. {
  572. g_fIsLoggingEnabled = FALSE;
  573. g_fIsDumpingEnabled = FALSE;
  574. g_fLegitOnly = TRUE;
  575. g_verbosity = MUTE;
  576. g_cLoops = -1;
  577. }
  578. if( CmdLineParser.GetNextFlag( pwcBadParam ) )
  579. {
  580. Usage( argv[0] );
  581. printf( "ERROR: Unknown command line switch : %ls\r\n", pwcBadParam );
  582. exit( -1 );
  583. }
  584. // Done processing switches
  585. // Allocate memory for the thread handles
  586. hProducerThreads = NEW HANDLE[ iNbrProducers ];
  587. hConsumerThreads = NEW HANDLE[ iNbrConsumers ];
  588. // Launch the producer threads
  589. for( iLoop = 0; iLoop < iNbrProducers; iLoop++ )
  590. {
  591. while( NULL == ( hProducerThreads[ iLoop ] = CreateThread(
  592. NULL, 0, &Producer, (void*)rgszInputFileName[ iLoop ],
  593. 0, &dwThreadID ) ) )
  594. {
  595. Sleep( dwSleepTime );
  596. }
  597. }
  598. // Launch the consumer threads
  599. for( iLoop = 0; iLoop < iNbrConsumers; iLoop++ )
  600. {
  601. while( NULL == ( hConsumerThreads[ iLoop ] = CreateThread(
  602. NULL, 0, &Consumer, (void*)IntToPtr(iLoop), 0, &dwThreadID ) ) )
  603. {
  604. Sleep( dwSleepTime );
  605. }
  606. }
  607. // Wait for all the producers to finish
  608. WaitForMultipleObjects( (DWORD) iNbrProducers,
  609. hProducerThreads,
  610. TRUE,
  611. INFINITE );
  612. // Signal the Consumer threads to finish.
  613. g_WorkQueue.Done();
  614. // Wait for the consumer threads to finish
  615. WaitForMultipleObjects( (DWORD) iNbrConsumers,
  616. hConsumerThreads,
  617. TRUE,
  618. INFINITE );
  619. }
  620. catch (...)
  621. {
  622. }
  623. {
  624. // Close all the handles
  625. if( NULL != hProducerThreads )
  626. {
  627. for( iLoop=0; iLoop < iNbrProducers; iLoop++ )
  628. {
  629. if( NULL != hProducerThreads[ iLoop ] )
  630. {
  631. (void)CloseHandle( hProducerThreads[ iLoop ] );
  632. hProducerThreads[ iLoop ] = NULL;
  633. }
  634. }
  635. delete[] hProducerThreads;
  636. hProducerThreads = NULL;
  637. }
  638. if( NULL != hConsumerThreads )
  639. {
  640. for( iLoop = 0; iLoop < iNbrConsumers; iLoop++ )
  641. {
  642. if( NULL != hConsumerThreads[ iLoop ] )
  643. {
  644. (void)CloseHandle( hConsumerThreads[ iLoop ] );
  645. hConsumerThreads[ iLoop ] = NULL;
  646. }
  647. }
  648. delete[] hConsumerThreads;
  649. hConsumerThreads = NULL;
  650. }
  651. if( NULL != rgszInputFileName )
  652. {
  653. delete[] rgszInputFileName;
  654. rgszInputFileName = NULL;
  655. }
  656. // If there is a shared log, report stats and quit
  657. if( NULL != g_pLog )
  658. {
  659. g_pLog->ReportStats( );
  660. delete g_pLog;
  661. g_pLog = NULL;
  662. }
  663. // Restore the old new handler
  664. _set_new_handler( pfOldNewHandler );
  665. // Shut down CI to prevent memory leaks
  666. CIShutdown();
  667. }
  668. return( 0 );
  669. } //main