home *** CD-ROM | disk | FTP | other *** search
- /*++
-
- Copyright (C) 1992-1997 Microsoft Corporation
-
- Module Name:
-
- pdc.c
-
- Abstract:
-
- This is the main source file for the Windows/NT PDC API demonstration
- program. This program demonstrates how to use many of the advanced
- operating system features provided by the Win32 API set on Windows/NT.
-
- This file and its corresponding header file, pdc.h, can be found
- in the sample programs directory of the PDC CD-ROM.
-
- This program has a real purpose, although the implementation is
- somewhat contrived in order to demonstrate the various operating
- system features of Windows/NT.
-
- The features that this program demonstrate are:
-
- - Creating multiple threads, using critical sections
- and semaphores for synchronization.
-
- - Thread termination.
-
- - Virtual memory, commitment vs reservation.
-
- - Structured exception handling, including finally
- clauses and an exception filter procedure.
-
- - Enumeration of directory entries.
-
- - Mapped file I/O
-
- - Asynchronous file I/O via completion routine.
-
- - Synchronous file I/O
-
- PDC is a character mode program for searching the files in a
- directory tree for a match against a pattern. It uses multiple
- threads to do it's work, with each thread processing a file at a
- time, accumulating it's matches and outputting them to standard
- output contiguously when it is done searching a file.
-
- The command line syntax is:
-
- Usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]
-
- where:
-
- -h - prints this message.
-
- -v - generates verbose output.
-
- -y - ignores case when doing comparisons.
-
- -a - specifies that the program should use asynchronous file
- I/O to read the files being searched.
-
- -s - specifies that the program should use synchronous file
- I/O to read the files being searched.
-
- -m - specifies that the program should use mapped file I/O
- to read the files being searched.
-
- -t - specifies the number of threads to use when doing the
- search. Default is 4 * the number of processors.
-
- SearchString - specifies the text to search for. Enclose in
- quotes if it contains spaces or punctuation.
-
- DirectoryPath - specifies the root of the tree to begin the
- search at. Defaults to the current directory.
-
-
- --*/
-
- #include "pdc.h"
-
-
- int
- main(
- int argc,
- char *argv[]
- )
-
- /*++
-
- Routine Description:
-
- This is the main procedure for the PDC program, and is called by
- the C Runtime startup code when the program starts.
-
- Arguments:
-
- argc - number of argumments in the argv array.
-
- argv - pointer to an array of null terminated string pointers.
-
- Return Value:
-
- Process exit status. The value returned by this function will
- be used as the exit code parameter passed to ExitProcess.
-
- --*/
-
- {
- SYSTEM_INFO SystemInformation;
- PWORK_QUEUE WorkQueue;
-
- //
- // Query the number of processors from the system and
- // default the number of worker threads to 4 times that.
- //
-
- GetSystemInfo( &SystemInformation );
- NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4;
-
- //
- // Process the arguments given on the command line.
- //
-
- if (!ProcessCommandLineArguments( argc, argv )) {
- exit( 1 );
- }
-
- //
- // Allocate a thread local storage slot for use by our worker
- // thread routine (ProcessRequest). This call reserves a
- // 32-bit slot in the thread local storage array for every
- // thread in this process. Remember the slot index in a global
- // variable for use by our worker thread routine.
- //
-
- TlsIndex = TlsAlloc();
- if (TlsIndex == 0xFFFFFFFF) {
- fprintf( stderr, "PDC: Unable to allocated thread local storage.\n" );
- exit( 1 );
- }
-
-
- //
- // Create a work queue, which will create the specified number of threads
- // to process.
- //
-
- WorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest );
- if (WorkQueue == NULL) {
- fprintf( stderr, "PDC: Unable to create %u worker threads.\n", NumberOfWorkerThreads );
- exit( 1 );
- }
-
- //
- // If using asynchronous I/O, create an event that will be signalled
- // when there are no more outstanding I/O requests. The event is
- // a manual reset event, that once signalled via SetEvent, will
- // remain signalled until ResetEvent is called.
- //
-
- if (ASyncIO) {
- IoCompletedEvent = CreateEvent( NULL, // Not inherited
- TRUE, // Manual reset
- FALSE, // Initially reset
- NULL // No name
- );
- }
-
- //
- // Now walk the directory tree, which will call our procedure
- // (QueueSearchFile) for each directory and file in the tree.
- //
-
- EnumerateDirectoryTree( DirectoryPath,
- QueueSearchFile,
- WorkQueue
- );
-
- //
- // Done walking the tree. If using asynchronous I/O, wait for all of
- // the outstanding I/O requests to be completed.
- //
-
- if (ASyncIO) {
- //
- // We use an alertable wait in a loop, as I/O completion
- // will terminate the wait, even through the event we
- // are waiting on is not signalled.
- //
-
- while (WaitForSingleObjectEx( IoCompletedEvent,
- 0xFFFFFFFF,
- TRUE
- ) == WAIT_IO_COMPLETION
- ) {
- ;
- }
- }
-
- //
- // All done, destroy the work queue. This will wait for the work queues
- // to empty before terminating the worker threads and destroying the
- // queue.
- //
-
- DestroyWorkQueue( WorkQueue );
-
- if (Verbose && MatchedLineCount) {
- fprintf( stderr,
- "Found %u lines with matches in %u files, out of %u files searched.\n",
- MatchedLineCount,
- MatchedFileCount,
- SearchedFileCount
- );
- }
-
- return 0;
- }
-
-
-
- VOID
- QueueSearchFile(
- LPSTR Path,
- PWIN32_FIND_DATA FindFileData,
- PVOID EnumerateParameter
- )
-
- /*++
-
- Routine Description:
-
- This is the directory enumeration function. It is called by the
- EnumerateDirectoryTree function once for each file and directory
- in the tree.
-
- This function, if it decides it wants to search the file, will
- open the file and then, depending upon the I/O method selected via
- the command line, will:
-
- - map the file PAGE_READONLY for mapped file I/O
-
- - read the file into an allocated buffer for synchronous
- I/O.
-
- - will allocate the buffer and start a read operation
- to read the entire file into the buffer. A completion
- routine will be invoked when the read completes, possibly
- in another thread context.
-
- Finally it will queue a search request to the work queue, with the
- relevant information contained in the request. For asynchronous
- I/O, the search request is allocated and initialized here but is
- not actually queued to the work queue until the I/O completion
- routine has been called.
-
- Arguments:
-
- Path - Supplies a pointer to a null terminated string that contains
- the fully qualified path of the file or directory.
-
- FindFileData - Supplies the directory information associated
- with the file or directory specified by the Path argument.
-
- EnumerateParameter - Uninterpreted 32-bit value. Not used.
-
- Return Value:
-
- None.
-
- --*/
-
- {
- PWORK_QUEUE WorkQueue = (PWORK_QUEUE)EnumerateParameter;
- PSEARCH_REQUEST SearchRequest;
- HANDLE File;
- HANDLE Mapping;
- LPVOID FileData;
- DWORD FileSize;
-
- //
- // Ignore directories or zero length files, as there
- // is nothing to search in these cases.
- //
-
- if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
- !(FileSize = FindFileData->nFileSizeLow)
- ) {
- return;
- }
-
- //
- // Open the file using the fully qualified path. Specify the
- // sequential scan hint to the cache manager and if asynchronous
- // I/O will be used, specified the overlapped flag as well.
- //
-
- File = CreateFile( Path,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN |
- (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
- NULL
- );
-
- //
- // Since NULL might be a valid file object handle, failure
- // is indicated by a special return value.
- //
-
- if (File == INVALID_HANDLE_VALUE) {
- fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
- Path,
- GetLastError()
- );
-
- return;
- }
-
- //
- // File successfully opened for read access.
- //
- if (MappedFileIO) {
- //
- // If mapped file I/O, create a file mapping object, backed by
- // the file we just opened. Make the default page protection
- // for the mapping be readonly.
- //
-
- Mapping = CreateFileMapping( File,
- NULL,
- PAGE_READONLY,
- 0,
- 0,
- NULL
- );
-
- //
- // Okay to close the file handle now, since the mapping object
- // has a reference to the file that will cause the open file
- // object to remain until the reference is destroyed. Note that
- // any sharing information is lost at this point, so somebody
- // could come in an open it for write access.
- //
-
- CloseHandle( File );
-
- //
- // Here, a null value indicates an error.
- //
-
- if (Mapping == NULL) {
- fprintf( stderr, "%s(0) : error %u: Unable to create mapping object.\n",
- Path,
- GetLastError()
- );
- return;
- }
-
- //
- // Finally, map a view of the file, using the mapping object
- // just created, into the address space of this process.
- // The offset and size are zero, which means to map the
- // entire file.
- //
-
- FileData = MapViewOfFile( Mapping,
- FILE_MAP_READ,
- 0,
- 0,
- 0
- );
-
- //
- // Okay to close the mapping object handle now, it will remained
- // referenced as long as the view remains mapped.
- //
-
- CloseHandle( Mapping );
-
- //
- // A null value indicates the map operation failed.
- //
-
- if (FileData == NULL) {
- fprintf( stderr, "%s(0) : error %u: Unable to map file.\n",
- Path,
- GetLastError()
- );
- return;
- }
-
- //
- // All done mapping the file. Set the File handle to NULL as
- // it has been closed already. Both the file object and mapping
- // objects created above will be freed when the map view is
- // unmapped.
- //
-
- File = NULL;
- }
- else {
- //
- // Not using mapped I/O, so allocate a buffer big enough to
- // contain the entire file.
- //
-
- FileData = VirtualAlloc( NULL,
- FileSize,
- MEM_COMMIT,
- PAGE_READWRITE
- );
- if (FileData == NULL) {
- fprintf( stderr, "%s(0) : error %u: Unable to allocate memory to contain file.\n",
- Path,
- GetLastError()
- );
- CloseHandle( File );
- return;
- }
- }
-
- //
- // We have successfully opened the file and are ready to go.
- // Allocate space for a search request and fill it in
- // with the information needed by the ProcessSearchFile
- // function when it runs.
- //
-
- SearchRequest = LocalAlloc( LMEM_ZEROINIT,
- sizeof( *SearchRequest ) +
- strlen( Path ) + 1
- );
- if (SearchRequest == NULL) {
- fprintf( stderr, "PDC: Out of memory\n" );
- exit( 1 );
- }
-
- SearchRequest->WorkItem.Reason = WORK_ITEM;
- SearchRequest->File = File;
- SearchRequest->FileSize = FileSize;
- SearchRequest->FileData = FileData;
- strcpy( SearchRequest->FullPathName, Path );
-
- if (!ASyncIO) {
- //
- // If not using asynchronous I/O, then queue the search
- // request to the work queue.
- //
-
- QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
- }
- else {
- //
- // Using asynchronous I/O, so queue the read operation.
- // The file handle must remain open while the read operation
- // is pending.
- //
-
- if (!ReadFileEx( File,
- FileData,
- FileSize,
- &SearchRequest->OverlappedIO,
- (LPOVERLAPPED_COMPLETION_ROUTINE) ProcessReadFileCompletion
- )
- ) {
- fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n",
- Path,
- GetLastError()
- );
-
- VirtualFree( FileData, 0, MEM_RELEASE );
- CloseHandle( File );
- LocalFree( SearchRequest );
- return;
- }
-
-
- //
- // Successfully queued the read operation. Keep a count
- // of outstanding read operations so we know when it is
- // okay to terminate.
- //
-
- OutstandingIOOperations += 1;
- }
-
- //
- // Return back to the EnumerateDirectoryTree function so that it
- // can call us with the next file or directrry.
- //
-
- return;
- }
-
-
-
- VOID
- ProcessRequest(
- IN PWORK_QUEUE_ITEM WorkItem
- )
-
- /*++
-
- Routine Description:
-
- This function is called whenever a work item is removed from
- the work queue by one of the worker threads. Which worker
- thread context this function is called in is arbitrary.
-
- This functions keeps a pointer to state information in
- thread local storage.
-
- This function is called once at the beginning with a
- special initialization call. During this call, this
- function allocates space for state information and
- remembers the pointer to the state information in
- a Thread Local Storage (TLS) slot.
-
- This function is called once at the end with a special
- termination call. During this call, this function
- frees the state information allocated during the
- initialization call.
-
- In between these two calls are zero or more calls to
- handle a work item. The work item is a search request
- which is handled by the ProcessSearchFile function.
-
- Arguments:
-
- WorkItem - Supplies a pointer to the work item just removed
- from the work queue. It is the responsibility of this
- routine to free the memory used to hold the work item.
-
- Return Value:
-
- None.
-
- --*/
-
- {
- DWORD BytesWritten;
- PSEARCH_REQUEST_STATE State;
- PSEARCH_REQUEST SearchRequest;
- CHAR MessageBuffer[ 2 * MAX_PATH ];
-
- if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
- //
- // First time initialization call. Allocate space for
- // state information.
- //
-
- State = LocalAlloc( LMEM_ZEROINIT,
- sizeof( *State )
- );
-
- if (State != NULL) {
- //
- // Now create a virtual buffer, with an initial commitment
- // of zero and a maximum commitment of 128KB. This buffer
- // will be used to accumulate the matched strings output
- // during the search of a single file. This is so the
- // output can be written to standard output with a single
- // write call, thus insuring that it remains contiguous
- // in the output stream, and is not intermingled with the
- // output of the other worker threads.
- //
-
- if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
- //
- // The CurrentOutput field of the state block is
- // a pointer to where the next output goes in the
- // buffer. It is initialized here and reset each
- // time the buffer is flushed to standard output.
- //
-
- State->CurrentOutput = State->Buffer.Base;
- }
- else {
- LocalFree( State );
- State = NULL;
- }
- }
-
- //
- // Remember the pointer to the state informaiton
- // thread local storage.
- //
-
- TlsSetValue( TlsIndex, State );
- return;
- }
-
- //
- // Here to handle a work item or special terminate call.
- // Get the state pointer from thread local storage.
- //
-
- State = (PSEARCH_REQUEST_STATE)TlsGetValue( TlsIndex );
- if (State == NULL) {
- return;
- }
-
- //
- // If this is the special terminate work item, free the virtual
- // buffer and state block allocated above and set the thread
- // local storage value to NULL. Return to caller.
- //
-
- if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
- FreeVirtualBuffer( &State->Buffer );
- LocalFree( State );
- TlsSetValue( TlsIndex, NULL );
- return;
- }
-
- //
- // If not an initialize or terminate work item, then must be a
- // search request. Calculate the address of the search request
- // block, based on the position of the WorkItem field in the
- // SEARCH_REQUEST structure.
- //
-
- SearchRequest = CONTAINING_RECORD( WorkItem, SEARCH_REQUEST, WorkItem );
-
- //
- // Actual search operation is protected by a try ... except
- // block so that any attempts to store into the virtual buffer
- // will be handled correctly by extending the virtual buffer.
- //
-
- _try {
- //
- // Perform the search against this file.
- //
-
- ProcessSearchFile( SearchRequest, State );
-
- //
- // Done with this file. If using asynchronous I/O, decrement the
- // count of outstanding I/O operations and it if goes to zero,
- // then signal the IoCompletedEvent as there are no more outstanding
- // I/O operations.
- //
-
- if (ASyncIO && InterlockedDecrement( &OutstandingIOOperations ) == 0) {
- SetEvent( IoCompletedEvent );
- }
-
- //
- // If any output was written to the virtual buffer,
- // flush the output to standard output. Trim the
- // virtual buffer back to zero committed pages.
- //
-
- if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
- WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
- State->Buffer.Base,
- State->CurrentOutput - (LPSTR)State->Buffer.Base,
- &BytesWritten,
- NULL
- );
-
- TrimVirtualBuffer( &State->Buffer );
- State->CurrentOutput = (LPSTR)State->Buffer.Base;
- }
- }
-
- _except( VirtualBufferExceptionFilter( GetExceptionCode(),
- GetExceptionInformation(),
- &State->Buffer
- )
- ) {
-
- //
- // We will get here if the exception filter was unable to
- // commit the memory.
- //
-
- WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
- MessageBuffer,
- sprintf( MessageBuffer,
- "%s(0) : error 0: too many matches for file\n",
- SearchRequest->FullPathName
- ),
- &BytesWritten,
- NULL
- );
- }
-
- //
- // Free the storage used by the SearchRequest
- //
-
- LocalFree( SearchRequest );
-
- //
- // All done with this request. Return to the worker thread that
- // called us.
- //
-
- return;
- }
-
-
-
- VOID
- ProcessReadFileCompletion(
- DWORD dwErrorCode,
- DWORD dwNumberOfBytesTransfered,
- LPOVERLAPPED lpOverlapped
- )
-
- /*++
-
- Routine Description:
-
- This function is called whenever an asynchronous I/O operation,
- queued by the previous function, completes. This function
- calulates the address of the search request block and then
- queue the request to the work queue, now that the data is
- in memory.
-
- Arguments:
-
- dwErrorCode - Supplies the error code that the I/O completed with.
-
- dwNumberOfBytesTransfered - Supplies the actual number of bytes
- transferred.
-
- lpOverlapped - Supplies a pointer to the structure given to
- ReadFileEx when the I/O operation was queued.
-
- Return Value:
-
- None.
-
- --*/
-
- {
- PSEARCH_REQUEST SearchRequest;
-
- //
- // Since the data we need is now in memory, queue the search
- // request to the work queue.
- //
-
- SearchRequest = CONTAINING_RECORD( lpOverlapped, SEARCH_REQUEST, OverlappedIO );
- QueueWorkItem( SearchRequest->WorkItem.WorkQueue, &SearchRequest->WorkItem );
- }
-
-
-
- VOID
- ProcessSearchFile(
- IN PSEARCH_REQUEST SearchRequest,
- IN PSEARCH_REQUEST_STATE State
- )
-
- /*++
-
- Routine Description:
-
- This function performs the actual search of the contents of the
- passed file for the search string given on the command line.
- If we are using synchronous I/O, then do the read operation
- now.
-
- Search the contents of the file for any matches, and accumulate
- the match output in the virtual buffer using sprintf, which is
- multi-thread safe, even with the single threaded version of
- the libraries.
-
- Arguments:
-
- SearchRequest - Supplies a pointer to the search request which
- contains the relevant information.
-
- State - Supplies a pointer to state information for the current
- thread.
-
- Return Value:
-
- None.
-
- --*/
-
- {
- LPSTR FileData, s, s1, BegLine, EndLine, EndOfFile;
- DWORD LineNumber;
- DWORD MatchesFound;
- DWORD BytesRead;
-
- // Get a pointer to the beginning of the file data in memory
- // and calculate the address of the end of file point in
- // memory.
- //
-
- FileData = SearchRequest->FileData;
- EndOfFile = FileData + SearchRequest->FileSize;
-
- //
- // If using synchronous I/O, then we have not read in the
- // file contents yet, so issue the synchronous read to get
- // the data into memory.
- //
-
- if (SyncIO) {
- if (!ReadFile( SearchRequest->File,
- FileData,
- SearchRequest->FileSize,
- &BytesRead,
- NULL
- ) ||
- BytesRead != SearchRequest->FileSize
- ) {
- State->CurrentOutput += sprintf( State->CurrentOutput,
- "%s(0) : error %u: Unable to read file contents.\n",
- SearchRequest->FullPathName,
- GetLastError()
- );
-
- CloseHandle( SearchRequest->File );
- return;
- }
- }
-
- //
- // Close any open file handle associated with this request.
- //
-
- if (SearchRequest->File != NULL) {
- CloseHandle( SearchRequest->File );
- }
-
- //
- // Search the file contents, keeping track of line breaks
- // so we can tell the line number of each match.
- //
-
- s = FileData;
- LineNumber = 0;
- MatchesFound = 0;
- while (s < EndOfFile) {
- BegLine = s;
- while (s < EndOfFile && *s != '\n') {
- s++;
- }
-
- if (*s == '\n') {
- LineNumber += 1;
- EndLine = s - 1;
- if (EndLine > BegLine && EndLine[ -1 ] == '\r') {
- EndLine -= 1;
- }
-
- s1 = BegLine;
- while (s1 < (EndLine - SearchStringLength)) {
- if (!(SearchFunction)( s1, SearchString, SearchStringLength )) {
- //
- // We have a match for this line. Append the
- // output to the virtual buffer and update the
- // current output pointer.
- //
-
- State->CurrentOutput += sprintf( State->CurrentOutput,
- "%s(%u) : %.*s\n",
- SearchRequest->FullPathName,
- LineNumber,
- EndLine - BegLine,
- BegLine
- );
- MatchesFound += 1;
- break;
- }
-
- s1++;
- }
-
- s++;
- }
- }
-
- if (MatchesFound) {
- MatchedLineCount += MatchesFound;
- MatchedFileCount += 1;
- }
- SearchedFileCount += 1;
-
- //
- // All done with the file contents. Discard it either by
- // unmapping the view of the file in the case of mapped file
- // I/O or free the virtual memory for other types of I/O
- //
-
- if (MappedFileIO) {
- if (!UnmapViewOfFile( FileData )) {
- State->CurrentOutput += sprintf( State->CurrentOutput,
- "%s(%u) : UnmapViewOfFile( %08x ) failed, error == %u\n",
- SearchRequest->FullPathName,
- LineNumber,
- FileData,
- GetLastError()
- );
- }
- }
- else {
- VirtualFree( FileData, 0, MEM_RELEASE );
- }
- }
-
-
- PWORK_QUEUE
- CreateWorkQueue(
- IN DWORD NumberOfWorkerThreads,
- IN PWORKER_ROUTINE WorkerRoutine
- )
-
- /*++
-
- Routine Description:
-
- This function creates a work queue, with the specified number of
- threads to service work items placed in the queue. Work items
- are removed from the queue in the same order that they are placed
- in the queue.
-
- Arguments:
-
- NumberOfWorkerThreads - Specifies how many threads this function
- should create to process work items placed in the queue.
- Must be greater than 0 and less than 128.
-
- WorkerRoutine - Specifies the address of a routine to call
- for each work item as it is removed from the queue. The
- thread context the routine is called in is undefined.
-
- Return Value:
-
- A pointer to the work queue. Returns NULL if unable to create
- the work queue and its worker threads. Extended error information
- is available from GetLastError()
-
- --*/
-
- {
- PWORK_QUEUE WorkQueue;
- HANDLE Thread;
- DWORD ThreadId;
- DWORD i;
-
- //
- // Allocate space for the work queue, which includes an
- // array of thread handles.
- //
-
- WorkQueue = LocalAlloc( LMEM_ZEROINIT,
- sizeof( *WorkQueue ) +
- (NumberOfWorkerThreads * sizeof( HANDLE ))
- );
- if (WorkQueue == NULL) {
- return NULL;
- }
-
- //
- // The work queue is controlled by a counting semaphore that
- // is incremented each time a work item is placed in the queue
- // and decremented each time a worker thread wakes up to remove
- // an item from the queue.
- //
-
- if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 100000, NULL )) {
- //
- // Mutual exclusion between the worker threads accessing
- // the work queue is done with a critical section.
- //
-
- InitializeCriticalSection( &WorkQueue->CriticalSection );
-
- //
- // The queue itself is just a doubly linked list, where
- // items are placed in the queue at the tail of the list
- // and removed from the queue from the head of the list.
- //
-
- InitializeListHead( &WorkQueue->Queue );
-
- //
- // Removed the address of the supplied worker function
- // in the work queue structure.
- //
-
- WorkQueue->WorkerRoutine = WorkerRoutine;
-
- //
- // Now create the requested number of worker threads.
- // The handle to each thread is remembered in an
- // array of thread handles in the work queue structure.
- //
-
- for (i=0; i<NumberOfWorkerThreads; i++) {
- Thread = CreateThread( NULL,
- 0,
- (LPTHREAD_START_ROUTINE) WorkerThread,
- WorkQueue,
- 0,
- &ThreadId
- );
- if (Thread == NULL) {
- break;
- }
- else {
- WorkQueue->NumberOfWorkerThreads++;
- WorkQueue->WorkerThreads[ i ] = Thread;
- SetThreadPriority( Thread, THREAD_PRIORITY_ABOVE_NORMAL );
- }
- }
-
- //
- // If we successfully created all of the worker threads
- // then return the address of the work queue structure
- // to indicate success.
- //
-
- if (i == NumberOfWorkerThreads) {
- return WorkQueue;
- }
- }
-
- //
- // Failed for some reason. Destroy whatever we managed
- // to create and return failure to the caller.
- //
-
- DestroyWorkQueue( WorkQueue );
- return NULL;
- }
-
-
-
- VOID
- DestroyWorkQueue(
- IN OUT PWORK_QUEUE WorkQueue
- )
-
- /*++
-
- Routine Description:
-
- This function destroys a work queue created with the CreateWorkQueue
- functions. It attempts to shut down the worker threads cleanly
- by queueing a terminate work item to each worker thread. It then
- waits for all the worker threads to terminate. If the wait is
- not satisfied within 30 seconds, then it goes ahead and terminates
- all of the worker threads.
-
- Arguments:
-
- WorkQueue - Supplies a pointer to the work queue to destroy.
-
- Return Value:
-
- None.
-
- --*/
-
- {
- DWORD i;
- DWORD rc;
-
- //
- // If the semaphore handle field is not NULL, then there
- // may be threads to terminate.
- //
-
- if (WorkQueue->Semaphore != NULL) {
- //
- // Set the termiating flag in the work queue and
- // signal the counting semaphore by the number
- // worker threads so they will all wake up and
- // notice the terminating flag and exit.
- //
-
- EnterCriticalSection( &WorkQueue->CriticalSection );
- _try {
- WorkQueue->Terminating = TRUE;
- ReleaseSemaphore( WorkQueue->Semaphore,
- WorkQueue->NumberOfWorkerThreads,
- NULL
- );
- }
- _finally {
- LeaveCriticalSection( &WorkQueue->CriticalSection );
- }
-
- //
- // Wait for all worker threads to wake up and see the
- // terminate flag and then terminate themselves. Timeout
- // the wait after 30 seconds.
- //
-
- while (TRUE) {
- rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
- WorkQueue->WorkerThreads,
- TRUE,
- 30000,
- TRUE
- );
- if (rc == WAIT_IO_COMPLETION) {
- //
- // If we came out of the wait because an I/O
- // completion routine was called, reissue the
- // wait.
- //
- continue;
- }
- else {
- break;
- }
- }
-
- //
- // Now close our thread handles so they will actually
- // evaporate. If the wait above was unsuccessful,
- // then first attempt to force the termination of
- // each worker thread prior to closing the handle.
- //
-
- for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
- if (rc != NO_ERROR) {
- TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
- }
-
- CloseHandle( WorkQueue->WorkerThreads[ i ] );
- }
-
- //
- // All threads stopped, all thread handles closed. Now
- // delete the critical section and close the semaphore
- // handle.
- //
-
- DeleteCriticalSection( &WorkQueue->CriticalSection );
- CloseHandle( WorkQueue->Semaphore );
- }
-
- //
- // Everything done, now free the memory used by the work queue.
- //
-
- LocalFree( WorkQueue );
- return;
- }
-
-
-
- BOOL
- QueueWorkItem(
- IN OUT PWORK_QUEUE WorkQueue,
- IN PWORK_QUEUE_ITEM WorkItem
- )
-
- /*++
-
- Routine Description:
-
- This function queues a work item to the passed work queue that is
- processed by one of the worker threads associated with the queue.
-
- Arguments:
-
- WorkQueue - Supplies a pointer to the work queue that is to
- receive the work item.
-
- WorkItem - Supplies a pointer to the work item to add the the queue.
- The work item structure contains a doubly linked list entry, the
- address of a routine to call and a parameter to pass to that
- routine. It is the routine's responsibility to reclaim the
- storage occupied by the WorkItem structure.
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- BOOL Result;
-
- //
- // Acquire the work queue critical section and insert the work item
- // in the queue and release the semaphore if the work item is not
- // already in the list.
- //
-
- EnterCriticalSection( &WorkQueue->CriticalSection );
- Result = TRUE;
- _try {
- WorkItem->WorkQueue = WorkQueue;
- InsertTailList( &WorkQueue->Queue, &WorkItem->List );
- Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
- }
- _finally {
- LeaveCriticalSection( &WorkQueue->CriticalSection );
- }
-
- return Result;
- }
-
-
-
- DWORD
- WorkerThread(
- LPVOID lpThreadParameter
- )
- {
- PWORK_QUEUE WorkQueue = (PWORK_QUEUE)lpThreadParameter;
- DWORD rc;
- WORK_QUEUE_ITEM InitWorkItem;
- PWORK_QUEUE_ITEM WorkItem;
-
- //
- // Call the worker routine with an initialize work item
- // to give it a change to initialize some per thread
- // state that will passed to it for each subsequent
- // work item.
- //
-
- InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
- (WorkQueue->WorkerRoutine)( &InitWorkItem );
- while( TRUE ) {
- _try {
-
- //
- // Wait until something is put in the queue (semaphore is
- // released), remove the item from the queue, mark it not
- // inserted, and execute the specified routine.
- //
-
- rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
- if (rc == WAIT_IO_COMPLETION) {
- continue;
- }
-
- EnterCriticalSection( &WorkQueue->CriticalSection );
- _try {
- if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
- break;
- }
-
- WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
- }
- _finally {
- LeaveCriticalSection( &WorkQueue->CriticalSection );
- }
-
- //
- // Execute the worker routine for this work item.
- //
-
- (WorkQueue->WorkerRoutine)( WorkItem );
- }
- _except( EXCEPTION_EXECUTE_HANDLER ) {
- //
- // Ignore any exceptions from worker routine.
- //
- }
- }
-
- InitWorkItem.Reason = WORK_TERMINATE_ITEM;
- (WorkQueue->WorkerRoutine)( &InitWorkItem );
-
- ExitThread( 0 );
- return 0; // This will exit this thread
- }
-
-
- BOOL
- CreateVirtualBuffer(
- OUT PVIRTUAL_BUFFER Buffer,
- IN DWORD CommitSize,
- IN DWORD ReserveSize OPTIONAL
- )
-
- /*++
-
- Routine Description:
-
- This function is called to create a virtual buffer. A virtual
- buffer is a contiguous range of virtual memory, where some initial
- prefix portion of the memory is committed and the remainder is only
- reserved virtual address space. A routine is provided to extend the
- size of the committed region incrementally or to trim the size of
- the committed region back to some specified amount.
-
- Arguments:
-
- Buffer - Pointer to the virtual buffer control structure that is
- filled in by this function.
-
- CommitSize - Size of the initial committed portion of the buffer.
- May be zero.
-
- ReserveSize - Amount of virtual address space to reserve for the
- buffer. May be zero, in which case amount reserved is the
- committed size plus one, rounded up to the next 64KB boundary.
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- SYSTEM_INFO SystemInformation;
-
- //
- // Query the page size from the system for rounding
- // our memory allocations.
- //
-
- GetSystemInfo( &SystemInformation );
- Buffer->PageSize = SystemInformation.dwPageSize;
-
- //
- // If the reserve size was not specified, default it by
- // rounding up the initial committed size to a 64KB
- // boundary. This is because the Win32 Virtual Memory
- // API calls always allocate virtual address space on
- // 64KB boundaries, so we might well have it available
- // for commitment.
- //
-
- if (!ARGUMENT_PRESENT( ReserveSize )) {
- ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
- }
-
- //
- // Attempt to reserve the address space.
- //
-
- Buffer->Base = VirtualAlloc( NULL,
- ReserveSize,
- MEM_RESERVE,
- PAGE_READWRITE
- );
- if (Buffer->Base == NULL) {
- //
- // Unable to reserve address space, return failure.
- //
-
- return FALSE;
- }
-
- //
- // Attempt to commit some initial portion of the reserved region.
- //
- //
-
- CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
- if (CommitSize == 0 ||
- VirtualAlloc( Buffer->Base,
- CommitSize,
- MEM_COMMIT,
- PAGE_READWRITE
- ) != NULL
- ) {
- //
- // Either the size of the committed region was zero or the
- // commitment succeeded. In either case calculate the
- // address of the first byte after the committed region
- // and the address of the first byte after the reserved
- // region and return successs.
- //
-
- Buffer->CommitLimit = (LPVOID)
- ((char *)Buffer->Base + CommitSize);
-
- Buffer->ReserveLimit = (LPVOID)
- ((char *)Buffer->Base + ReserveSize);
-
- return TRUE;
- }
-
- //
- // If unable to commit the memory, release the virtual address
- // range allocated above and return failure.
- //
-
- VirtualFree( Buffer->Base, 0, MEM_RELEASE );
- return FALSE;
- }
-
-
-
- BOOL
- ExtendVirtualBuffer(
- IN PVIRTUAL_BUFFER Buffer,
- IN LPVOID Address
- )
-
- /*++
-
- Routine Description:
-
- This function is called to extend the committed portion of a virtual
- buffer.
-
- Arguments:
-
- Buffer - Pointer to the virtual buffer control structure.
-
- Address - Byte at this address is committed, along with all memory
- from the beginning of the buffer to this address. If the
- address is already within the committed portion of the virtual
- buffer, then this routine does nothing. If outside the reserved
- portion of the virtual buffer, then this routine returns an
- error.
-
- Otherwise enough pages are committed so that the memory from the
- base of the buffer to the passed address is a contiguous region
- of committed memory.
-
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- DWORD NewCommitSize;
- LPVOID NewCommitLimit;
-
- //
- // See if address is within the buffer.
- //
-
- if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
- //
- // See if the address is within the committed portion of
- // the buffer. If so return success immediately.
- //
-
- if (Address < Buffer->CommitLimit) {
- return TRUE;
- }
-
- //
- // Address is within the reserved portion. Determine how many
- // bytes are between the address and the end of the committed
- // portion of the buffer. Round this size to a multiple of
- // the page size and this is the amount we will attempt to
- // commit.
- //
-
- NewCommitSize =
- ((DWORD)ROUND_UP( (DWORD)Address + 1, Buffer->PageSize ) -
- (DWORD)Buffer->CommitLimit
- );
-
- //
- // Attempt to commit the memory.
- //
-
- NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
- NewCommitSize,
- MEM_COMMIT,
- PAGE_READWRITE
- );
- if (NewCommitLimit != NULL) {
- //
- // Successful, so update the upper limit of the committed
- // region of the buffer and return success.
- //
-
- Buffer->CommitLimit = (LPVOID)
- ((DWORD)NewCommitLimit + NewCommitSize);
-
- return TRUE;
- }
- }
-
- //
- // Address is outside of the buffer, return failure.
- //
-
- return FALSE;
- }
-
-
- BOOL
- TrimVirtualBuffer(
- IN PVIRTUAL_BUFFER Buffer
- )
-
- /*++
-
- Routine Description:
-
- This function is called to decommit any memory that has been
- committed for this virtual buffer.
-
- Arguments:
-
- Buffer - Pointer to the virtual buffer control structure.
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- Buffer->CommitLimit = Buffer->Base;
- return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
- }
-
-
-
- BOOL
- FreeVirtualBuffer(
- IN PVIRTUAL_BUFFER Buffer
- )
- /*++
-
- Routine Description:
-
- This function is called to free all the memory that is associated
- with this virtual buffer.
-
- Arguments:
-
- Buffer - Pointer to the virtual buffer control structure.
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- //
- // Decommit and release all virtual memory associated with
- // this virtual buffer.
- //
-
- return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
- }
-
-
-
- int
- VirtualBufferExceptionFilter(
- IN DWORD ExceptionCode,
- IN PEXCEPTION_POINTERS ExceptionInfo,
- IN OUT PVIRTUAL_BUFFER Buffer
- )
-
- /*++
-
- Routine Description:
-
- This function is an exception filter that handles exceptions that
- referenced uncommitted but reserved memory contained in the passed
- virtual buffer. It this filter routine is able to commit the
- additional pages needed to allow the memory reference to succeed,
- then it will re-execute the faulting instruction. If it is unable
- to commit the pages, it will execute the callers exception handler.
-
- If the exception is not an access violation or is an access
- violation but does not reference memory contained in the reserved
- portion of the virtual buffer, then this filter passes the exception
- on up the exception chain.
-
- Arguments:
-
- ExceptionCode - Reason for the exception.
-
- ExceptionInfo - Information about the exception and the context
- that it occurred in.
-
- Buffer - Points to a virtual buffer control structure that defines
- the reserved memory region that is to be committed whenever an
- attempt is made to access it.
-
- Return Value:
-
- Exception disposition code that tells the exception dispatcher what
- to do with this exception. One of three values is returned:
-
- EXCEPTION_EXECUTE_HANDLER - execute the exception handler
- associated with the exception clause that called this filter
- procedure.
-
- EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
- handler to handle this exception.
-
- EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
- control to the instruction that caused the exception.
-
-
- --*/
-
- {
- LPVOID FaultingAddress;
-
- //
- // If this is an access violation touching memory within
- // our reserved buffer, but outside of the committed portion
- // of the buffer, then we are going to take this exception.
- //
-
- if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
- //
- // Get the virtual address that caused the access violation
- // from the exception record. Determine if the address
- // references memory within the reserved but uncommitted
- // portion of the virtual buffer.
- //
-
- FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
- if (FaultingAddress >= Buffer->CommitLimit &&
- FaultingAddress <= Buffer->ReserveLimit
- ) {
- //
- // This is our exception. Try to extend the buffer
- // to including the faulting address.
- //
-
- if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
- //
- // Buffer successfully extended, so re-execute the
- // faulting instruction.
- //
-
- return EXCEPTION_CONTINUE_EXECUTION;
- }
- else {
- //
- // Unable to extend the buffer. Stop searching
- // for exception handlers and execute the caller's
- // handler.
- //
-
- return EXCEPTION_EXECUTE_HANDLER;
- }
- }
- }
-
- //
- // Not an exception we care about, so pass it up the chain.
- //
-
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
-
- BOOL
- EnumerateDirectoryTree(
- LPSTR DirectoryPath,
- PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
- PVOID EnumerateParameter
- )
-
- /*++
-
- Routine Description:
-
- This function walks a directory tree, depth first, calling the
- passed enumeration routine for each directory and file found
- in the tree. The enumeration routine is passed the full path
- of the file, the directory information associated with the file
- and an enumeration parameter that is uninterpreted by this
- function.
-
- Arguments:
-
- DirectoryPath - Absolute or relative path to the directory that
- will is the root of the tree to enumerate.
-
- EnumerateRoutine - Pointer to an enumeration routine to call
- for each file and directory found.
-
- EnumerateParameter - Uninterpreted 32-bit value that is passed
- to the EnumerationRoutine each time it is called.
-
- Return Value:
-
- TRUE if operation was successful. Otherwise returns FALSE and
- extended error information is available from GetLastError()
-
- --*/
-
- {
- BOOL Result;
- VIRTUAL_BUFFER Buffer;
- PENUMERATE_DIRECTORY_STATE State;
- PENUMERATE_DIRECTORY_STACK Stack;
- WIN32_FIND_DATA FindFileData;
-
- //
- // Create a virtual buffer with an initial committed size of
- // our directory state buffer, and a maximum reserved size of
- // the longest possible full path based on the maximum depth
- // we handle and the maximum length of each path component.
- //
-
- if (!CreateVirtualBuffer( &Buffer,
- sizeof( ENUMERATE_DIRECTORY_STATE ),
- sizeof( ENUMERATE_DIRECTORY_STATE ) +
- MAX_DEPTH * MAX_PATH
- )
- ) {
- return FALSE;
- }
-
- //
- // This buffer will be used to maintain a stack of directory
- // search handles, as well as accumulate the full path string
- // as we descend the directory tree.
- //
-
- State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
- State->Depth = 0;
- Stack = &State->Stack[ 0 ];
-
- //
- // Enter a try ... finally block so we can insure that we clean
- // up after ourselves on exit.
- //
-
- _try {
- //
- // First translate the passed in DirectoryPath into a fully
- // qualified path. This path will be the initial value in
- // our path buffer. The initial allocation of the path buffer
- // is big enough for this initial request, so does not need
- // to be guarded by a try ... except clause.
- //
-
- if (GetFullPathName( DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd )) {
- //
- // Now enter a try ... except block that will be used to
- // manage the commitment of space in the path buffer as
- // we append subdirectory names and file names to it.
- // Using the virtual buffer allows us to handle full
- // path names up to 16KB in length, with an initial
- // allocation of 4KB.
- //
-
- _try {
- //
- // Walk the directory tree. The outer loop is executed
- // once for each directory in the tree.
- //
-
- while (TRUE) {
- startDirectorySearch:
- //
- // Find the end of the current path, and make sure
- // there is a trailing path separator.
- //
-
- Stack->PathEnd = strchr( State->Path, '\0' );
- if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
- *(Stack->PathEnd)++ = '\\';
- }
-
- //
- // Now append the wild card specification that will
- // let us enumerate all the entries in this directory.
- // Call FindFirstFile to find the first entry in the
- // directory.
- //
-
- strcpy( Stack->PathEnd, "*.*" );
- Stack->FindHandle = FindFirstFile( State->Path,
- &FindFileData
- );
- if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
- //
- // Entry found. Now loop through the entire
- // directory processing each entry found,
- // including the first one.
- //
- do {
- //
- // Ignore bogus pseudo-directories that are
- // returned by some file systems (e.g. FAT).
- //
-
- if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
- (!strcmp( FindFileData.cFileName, "." ) ||
- !strcmp( FindFileData.cFileName, ".." )
- )
- ) {
- continue;
- }
-
- //
- // Copy the file name portion from the current
- // directory entry to the last component in the
- // path buffer.
- //
-
- strcpy( Stack->PathEnd, FindFileData.cFileName );
-
- //
- // Call the supplied enumeration routine with the
- // full path we have built up in the path buffer,
- // the directory information for this directory
- // entry and the supplied enumeration parameter.
- //
-
- (*EnumerateRoutine)( State->Path, &FindFileData, EnumerateParameter );
-
- //
- // If this is entry is a subdirectory, then it is
- // time to recurse. Do this by incrementing the
- // stack pointer and depth and jumping to the top
- // of the outer loop to process current contents
- // of the path buffer as a fully qualified name of
- // a directory.
- //
-
- if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- Stack++;
- State->Depth++;
- goto startDirectorySearch;
- restartDirectorySearch: ;
- }
-
- //
- // Here to find the next entry in the current directory.
- //
- }
-
- while ( FindNextFile( Stack->FindHandle, &FindFileData ) );
-
- //
- // No more entries in the current directory, so close
- // the search handle and fall into the code that will
- // pop our stack of directory seacrh handles.
-
- FindClose( Stack->FindHandle );
- }
-
- //
- // Here when done with a directory. See if we are pushed
- // inside another directory. If not, then we are done
- // enumerating the whole tree, so break out of the loop.
- //
-
- if (!State->Depth) {
- Result = TRUE;
- break;
- }
-
- //
- // We were pushed within another directory search,
- // so pop the stack to restore its search handle
- // and path buffer position and resume the search
- // within that directory.
-
- State->Depth--;
- --Stack;
- goto restartDirectorySearch;
- }
- }
-
- //
- // Any of the code that appends to the path buffer within
- // the above try ... except clause can cause an access
- // violation if the path buffer becomes longer than its
- // current committed size. This exception filter
- // will dynamically commit additional pages as needed
- // and resume execution.
- //
-
- _except( VirtualBufferExceptionFilter( GetExceptionCode(),
- GetExceptionInformation(),
- &Buffer
- )
- ) {
- //
- // We will get here if the exception filter was unable to
- // commit the memory.
- //
-
- Result = FALSE;
- }
- }
- else {
- //
- // Initial GetFullPathName failed, so return a failure.
- //
-
- Result = FALSE;
- }
- }
- _finally {
- //
- // Here on our way out of the outer try ... finally block.
- // Make sure all our search handles have been closed and then
- // free the virtual buffer. The only way this code is not
- // executed is if code within the try ... finally block
- // called ExitThread or ExitProcess, or an external thread
- // or process terminated this thread or process.
- //
- // In the case of process death, this is not a problem, because
- // process terminate closes all open handles attached to the process
- // and frees all private virtual memory that is part of the address
- // space of the process.
- //
- // In the case ot thread death, the code below is not executed if
- // the thread terminates via ExitThread in the context of the
- // try .. finally or if an external thread, either in this process
- // or another process called TerminateThread on this thread.
- //
-
- while (State->Depth--) {
- --Stack;
- FindClose( Stack->FindHandle );
- }
-
- FreeVirtualBuffer( &Buffer );
- }
-
- return Result;
- }
-
-
- BOOL
- ProcessCommandLineArguments(
- int argc,
- char *argv[]
- )
- {
- BOOL Result;
- LPSTR s;
-
- Result = FALSE;
- _try {
- if (argc < 1) {
- return Result;
- }
-
- while (--argc) {
- s = *++argv;
- if (*s == '-' || *s == '/') {
- while (*++s) {
- switch( tolower( *s ) ) {
- case 'm':
- MappedFileIO = TRUE;
- break;
-
- case 'a':
- ASyncIO = TRUE;
- break;
-
- case 's':
- SyncIO = TRUE;
- break;
-
- case 'v':
- Verbose = TRUE;
- break;
-
- case 'y':
- IgnoreCase = TRUE;
- break;
-
- case 't':
- if (--argc) {
- NumberOfWorkerThreads = atoi( *++argv );
- if (NumberOfWorkerThreads > 0 && NumberOfWorkerThreads < 128) {
- break;
- }
- }
-
- // fall through if -t argument missing.
-
- case '?':
- case 'h':
- default:
- return Result;
- }
- }
- }
- else
- if (SearchString == NULL) {
- SearchString = s;
- }
- else
- if (DirectoryPath == NULL) {
- DirectoryPath = s;
- }
- else {
- return Result;
- }
- }
-
- if (SearchString == NULL) {
- return Result;
- }
-
- SearchStringLength = strlen( SearchString );
- if (SearchStringLength == 0) {
- return Result;
- }
-
- if (IgnoreCase) {
- SearchFunction = _strnicmp;
- }
- else {
- SearchFunction = strncmp;
- }
-
- if (DirectoryPath == NULL) {
- DirectoryPath = ".";
- }
-
- if (!(MappedFileIO || ASyncIO || SyncIO)) {
- MappedFileIO = TRUE;
- }
-
- if (Verbose) {
- fprintf( stderr, "Directory Tree: %s\n", DirectoryPath );
- fprintf( stderr, "Search String: '%s'\n", SearchString );
- fprintf( stderr, "Case %ssensitive\n", IgnoreCase ? "in" : "" );
- fprintf( stderr, "Number of Worker Threads: %u\n", NumberOfWorkerThreads );
- if (MappedFileIO) {
- fprintf( stderr, "Using Mapped File I/O\n" );
- }
- else
- if (ASyncIO) {
- fprintf( stderr, "Using ASynchronous File I/O\n" );
- }
- else
- if (MappedFileIO) {
- fprintf( stderr, "Using Synchronous File I/O\n" );
- }
- }
-
- Result = TRUE;
- return Result;
- }
- _finally {
- if (!Result) {
- fprintf( stderr, "usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]\n" );
- fprintf( stderr, "Where...\n" );
- fprintf( stderr, " -h - prints this message\n" );
- fprintf( stderr, " -v - generates verbose output\n" );
- fprintf( stderr, " -y - ignores case when doing comparision\n" );
- fprintf( stderr, " -t - specifies the number of threads to use (defaults to 4 * number of processors)\n" );
- fprintf( stderr, " -a - uses asynchronous file I/O\n" );
- fprintf( stderr, " -s - uses synchronous file I/O\n" );
- fprintf( stderr, " -m - uses mapped file I/O (default)\n" );
- fprintf( stderr, " SearchString - specifies the text to search for\n" );
- fprintf( stderr, " DirectoryPath - specifies the directory to start from (defaults to .)\n" );
- }
- }
- }
-