home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / io / asyncio / pdc.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  64KB  |  2,074 lines

  1. /*++
  2.  
  3. Copyright (C) 1992-1997 Microsoft Corporation
  4.  
  5. Module Name:
  6.  
  7.     pdc.c
  8.  
  9. Abstract:
  10.  
  11.     This is the main source file for the Windows/NT PDC API demonstration
  12.     program.  This program demonstrates how to use many of the advanced
  13.     operating system features provided by the Win32 API set on Windows/NT.
  14.  
  15.     This file and its corresponding header file, pdc.h, can be found
  16.     in the sample programs directory of the PDC CD-ROM.
  17.  
  18.     This program has a real purpose, although the implementation is
  19.     somewhat contrived in order to demonstrate the various operating
  20.     system features of Windows/NT.
  21.  
  22.     The features that this program demonstrate are:
  23.  
  24.         - Creating multiple threads, using critical sections
  25.           and semaphores for synchronization.
  26.  
  27.         - Thread termination.
  28.  
  29.         - Virtual memory, commitment vs reservation.
  30.  
  31.         - Structured exception handling, including finally
  32.           clauses and an exception filter procedure.
  33.  
  34.         - Enumeration of directory entries.
  35.  
  36.         - Mapped file I/O
  37.  
  38.         - Asynchronous file I/O via completion routine.
  39.  
  40.         - Synchronous file I/O
  41.  
  42.     PDC is a character mode program for searching the files in a
  43.     directory tree for a match against a pattern.  It uses multiple
  44.     threads to do it's work, with each thread processing a file at a
  45.     time, accumulating it's matches and outputting them to standard
  46.     output contiguously when it is done searching a file.
  47.  
  48.     The command line syntax is:
  49.  
  50.         Usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]
  51.  
  52.         where:
  53.  
  54.             -h - prints this message.
  55.  
  56.             -v - generates verbose output.
  57.  
  58.             -y - ignores case when doing comparisons.
  59.  
  60.             -a - specifies that the program should use asynchronous file
  61.                  I/O to read the files being searched.
  62.  
  63.             -s - specifies that the program should use synchronous file
  64.                  I/O to read the files being searched.
  65.  
  66.             -m - specifies that the program should use mapped file I/O
  67.                  to read the files being searched.
  68.  
  69.             -t - specifies the number of threads to use when doing the
  70.                  search.  Default is 4 * the number of processors.
  71.  
  72.             SearchString - specifies the text to search for.  Enclose in
  73.                            quotes if it contains spaces or punctuation.
  74.  
  75.             DirectoryPath - specifies the root of the tree to begin the
  76.                             search at.  Defaults to the current directory.
  77.  
  78.  
  79. --*/
  80.  
  81. #include "pdc.h"
  82.  
  83.  
  84. int
  85. main(
  86.     int argc,
  87.     char *argv[]
  88.     )
  89.  
  90. /*++
  91.  
  92. Routine Description:
  93.  
  94.     This is the main procedure for the PDC program, and is called by
  95.     the C Runtime startup code when the program starts.
  96.  
  97. Arguments:
  98.  
  99.     argc - number of argumments in the argv array.
  100.  
  101.     argv - pointer to an array of null terminated string pointers.
  102.  
  103. Return Value:
  104.  
  105.     Process exit status.  The value returned by this function will
  106.     be used as the exit code parameter passed to ExitProcess.
  107.  
  108. --*/
  109.  
  110. {
  111.     SYSTEM_INFO SystemInformation;
  112.     PWORK_QUEUE WorkQueue;
  113.  
  114.     //
  115.     // Query the number of processors from the system and
  116.     // default the number of worker threads to 4 times that.
  117.     //
  118.  
  119.     GetSystemInfo( &SystemInformation );
  120.     NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4;
  121.  
  122.     //
  123.     // Process the arguments given on the command line.
  124.     //
  125.  
  126.     if (!ProcessCommandLineArguments( argc, argv )) {
  127.         exit( 1 );
  128.         }
  129.  
  130.     //
  131.     // Allocate a thread local storage slot for use by our worker
  132.     // thread routine (ProcessRequest).  This call reserves a
  133.     // 32-bit slot in the thread local storage array for every
  134.     // thread in this process.  Remember the slot index in a global
  135.     // variable for use by our worker thread routine.
  136.     //
  137.  
  138.     TlsIndex = TlsAlloc();
  139.     if (TlsIndex == 0xFFFFFFFF) {
  140.         fprintf( stderr, "PDC: Unable to allocated thread local storage.\n" );
  141.         exit( 1 );
  142.         }
  143.  
  144.  
  145.     //
  146.     // Create a work queue, which will create the specified number of threads
  147.     // to process.
  148.     //
  149.  
  150.     WorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest );
  151.     if (WorkQueue == NULL) {
  152.         fprintf( stderr, "PDC: Unable to create %u worker threads.\n", NumberOfWorkerThreads );
  153.         exit( 1 );
  154.         }
  155.  
  156.     //
  157.     // If using asynchronous I/O, create an event that will be signalled
  158.     // when there are no more outstanding I/O requests.  The event is
  159.     // a manual reset event, that once signalled via SetEvent, will
  160.     // remain signalled until ResetEvent is called.
  161.     //
  162.  
  163.     if (ASyncIO) {
  164.         IoCompletedEvent = CreateEvent( NULL,   // Not inherited
  165.                                         TRUE,   // Manual reset
  166.                                         FALSE,  // Initially reset
  167.                                         NULL    // No name
  168.                                       );
  169.         }
  170.  
  171.     //
  172.     // Now walk the directory tree, which will call our procedure
  173.     // (QueueSearchFile) for each directory and file in the tree.
  174.     //
  175.  
  176.     EnumerateDirectoryTree( DirectoryPath,
  177.                             QueueSearchFile,
  178.                             WorkQueue
  179.                           );
  180.  
  181.     //
  182.     // Done walking the tree.  If using asynchronous I/O, wait for all of
  183.     // the outstanding I/O requests to be completed.
  184.     //
  185.  
  186.     if (ASyncIO) {
  187.         //
  188.         // We use an alertable wait in a loop, as I/O completion
  189.         // will terminate the wait, even through the event we
  190.         // are waiting on is not signalled.
  191.         //
  192.  
  193.         while (WaitForSingleObjectEx( IoCompletedEvent,
  194.                                       0xFFFFFFFF,
  195.                                       TRUE
  196.                                     ) == WAIT_IO_COMPLETION
  197.               ) {
  198.             ;
  199.             }
  200.         }
  201.  
  202.     //
  203.     // All done, destroy the work queue.  This will wait for the work queues
  204.     // to empty before terminating the worker threads and destroying the
  205.     // queue.
  206.     //
  207.  
  208.     DestroyWorkQueue( WorkQueue );
  209.  
  210.     if (Verbose && MatchedLineCount) {
  211.         fprintf( stderr,
  212.                  "Found %u lines with matches in %u files, out of %u files searched.\n",
  213.                  MatchedLineCount,
  214.                  MatchedFileCount,
  215.                  SearchedFileCount
  216.                );
  217.         }
  218.  
  219.     return 0;
  220. }
  221.  
  222.  
  223.  
  224. VOID
  225. QueueSearchFile(
  226.     LPSTR Path,
  227.     PWIN32_FIND_DATA FindFileData,
  228.     PVOID EnumerateParameter
  229.     )
  230.  
  231. /*++
  232.  
  233. Routine Description:
  234.  
  235.     This is the directory enumeration function.  It is called by the
  236.     EnumerateDirectoryTree function once for each file and directory
  237.     in the tree.
  238.  
  239.     This function, if it decides it wants to search the file, will
  240.     open the file and then, depending upon the I/O method selected via
  241.     the command line, will:
  242.  
  243.         - map the file PAGE_READONLY for mapped file I/O
  244.  
  245.         - read the file into an allocated buffer for synchronous
  246.           I/O.
  247.  
  248.         - will allocate the buffer and start a read operation
  249.           to read the entire file into the buffer.  A completion
  250.           routine will be invoked when the read completes, possibly
  251.           in another thread context.
  252.  
  253.     Finally it will queue a search request to the work queue, with the
  254.     relevant information contained in the request.  For asynchronous
  255.     I/O, the search request is allocated and initialized here but is
  256.     not actually queued to the work queue until the I/O completion
  257.     routine has been called.
  258.  
  259. Arguments:
  260.  
  261.     Path - Supplies a pointer to a null terminated string that contains
  262.         the fully qualified path of the file or directory.
  263.  
  264.     FindFileData - Supplies the directory information associated
  265.         with the file or directory specified by the Path argument.
  266.  
  267.     EnumerateParameter - Uninterpreted 32-bit value.  Not used.
  268.  
  269. Return Value:
  270.  
  271.     None.
  272.  
  273. --*/
  274.  
  275. {
  276.     PWORK_QUEUE WorkQueue = (PWORK_QUEUE)EnumerateParameter;
  277.     PSEARCH_REQUEST SearchRequest;
  278.     HANDLE File;
  279.     HANDLE Mapping;
  280.     LPVOID FileData;
  281.     DWORD FileSize;
  282.  
  283.     //
  284.     // Ignore directories or zero length files, as there
  285.     // is nothing to search in these cases.
  286.     //
  287.  
  288.     if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
  289.         !(FileSize = FindFileData->nFileSizeLow)
  290.        ) {
  291.         return;
  292.         }
  293.  
  294.     //
  295.     // Open the file using the fully qualified path.  Specify the
  296.     // sequential scan hint to the cache manager and if asynchronous
  297.     // I/O will be used, specified the overlapped flag as well.
  298.     //
  299.  
  300.     File = CreateFile( Path,
  301.                        GENERIC_READ,
  302.                        FILE_SHARE_READ,
  303.                        NULL,
  304.                        OPEN_EXISTING,
  305.                        FILE_FLAG_SEQUENTIAL_SCAN |
  306.                          (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
  307.                        NULL
  308.                      );
  309.  
  310.     //
  311.     // Since NULL might be a valid file object handle, failure
  312.     // is indicated by a special return value.
  313.     //
  314.  
  315.     if (File == INVALID_HANDLE_VALUE) {
  316.         fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
  317.                  Path,
  318.                  GetLastError()
  319.                );
  320.  
  321.         return;
  322.         }
  323.  
  324.     //
  325.     // File successfully opened for read access.
  326.     //
  327.     if (MappedFileIO) {
  328.         //
  329.         // If mapped file I/O, create a file mapping object, backed by
  330.         // the file we just opened.  Make the default page protection
  331.         // for the mapping be readonly.
  332.         //
  333.  
  334.         Mapping = CreateFileMapping( File,
  335.                                      NULL,
  336.                                      PAGE_READONLY,
  337.                                      0,
  338.                                      0,
  339.                                      NULL
  340.                                    );
  341.  
  342.         //
  343.         // Okay to close the file handle now, since the mapping object
  344.         // has a reference to the file that will cause the open file
  345.         // object to remain until the reference is destroyed.  Note that
  346.         // any sharing information is lost at this point, so somebody
  347.         // could come in an open it for write access.
  348.         //
  349.  
  350.         CloseHandle( File );
  351.  
  352.         //
  353.         // Here, a null value indicates an error.
  354.         //
  355.  
  356.         if (Mapping == NULL) {
  357.             fprintf( stderr, "%s(0) : error %u: Unable to create mapping object.\n",
  358.                      Path,
  359.                      GetLastError()
  360.                    );
  361.             return;
  362.             }
  363.  
  364.         //
  365.         // Finally, map a view of the file, using the mapping object
  366.         // just created, into the address space of this process.
  367.         // The offset and size are zero, which means to map the
  368.         // entire file.
  369.         //
  370.  
  371.         FileData = MapViewOfFile( Mapping,
  372.                                   FILE_MAP_READ,
  373.                                   0,
  374.                                   0,
  375.                                   0
  376.                                 );
  377.  
  378.         //
  379.         // Okay to close the mapping object handle now, it will remained
  380.         // referenced as long as the view remains mapped.
  381.         //
  382.  
  383.         CloseHandle( Mapping );
  384.  
  385.         //
  386.         // A null value indicates the map operation failed.
  387.         //
  388.  
  389.         if (FileData == NULL) {
  390.             fprintf( stderr, "%s(0) : error %u: Unable to map file.\n",
  391.                      Path,
  392.                      GetLastError()
  393.                    );
  394.             return;
  395.             }
  396.  
  397.         //
  398.         // All done mapping the file.  Set the File handle to NULL as
  399.         // it has been closed already.  Both the file object and mapping
  400.         // objects created above will be freed when the map view is
  401.         // unmapped.
  402.         //
  403.  
  404.         File = NULL;
  405.         }
  406.     else {
  407.         //
  408.         // Not using mapped I/O, so allocate a buffer big enough to
  409.         // contain the entire file.
  410.         //
  411.  
  412.         FileData = VirtualAlloc( NULL,
  413.                                  FileSize,
  414.                                  MEM_COMMIT,
  415.                                  PAGE_READWRITE
  416.                                );
  417.         if (FileData == NULL) {
  418.             fprintf( stderr, "%s(0) : error %u: Unable to allocate memory to contain file.\n",
  419.                      Path,
  420.                      GetLastError()
  421.                    );
  422.             CloseHandle( File );
  423.             return;
  424.             }
  425.         }
  426.  
  427.     //
  428.     // We have successfully opened the file and are ready to go.
  429.     // Allocate space for a search request and fill it in
  430.     // with the information needed by the ProcessSearchFile
  431.     // function when it runs.
  432.     //
  433.  
  434.     SearchRequest = LocalAlloc( LMEM_ZEROINIT,
  435.                                 sizeof( *SearchRequest ) +
  436.                                     strlen( Path ) + 1
  437.                               );
  438.     if (SearchRequest == NULL) {
  439.         fprintf( stderr, "PDC: Out of memory\n" );
  440.         exit( 1 );
  441.         }
  442.  
  443.     SearchRequest->WorkItem.Reason = WORK_ITEM;
  444.     SearchRequest->File = File;
  445.     SearchRequest->FileSize = FileSize;
  446.     SearchRequest->FileData = FileData;
  447.     strcpy( SearchRequest->FullPathName, Path );
  448.  
  449.     if (!ASyncIO) {
  450.         //
  451.         // If not using asynchronous I/O, then queue the search
  452.         // request to the work queue.
  453.         //
  454.  
  455.         QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
  456.         }
  457.     else {
  458.         //
  459.         // Using asynchronous I/O, so queue the read operation.
  460.         // The file handle must remain open while the read operation
  461.         // is pending.
  462.         //
  463.  
  464.         if (!ReadFileEx( File,
  465.                          FileData,
  466.                          FileSize,
  467.                          &SearchRequest->OverlappedIO,
  468.                          (LPOVERLAPPED_COMPLETION_ROUTINE) ProcessReadFileCompletion
  469.                        )
  470.            ) {
  471.             fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n",
  472.                      Path,
  473.                      GetLastError()
  474.                    );
  475.  
  476.             VirtualFree( FileData, 0, MEM_RELEASE );
  477.             CloseHandle( File );
  478.             LocalFree( SearchRequest );
  479.             return;
  480.             }
  481.  
  482.  
  483.         //
  484.         // Successfully queued the read operation.  Keep a count
  485.         // of outstanding read operations so we know when it is
  486.         // okay to terminate.
  487.         //
  488.  
  489.         OutstandingIOOperations += 1;
  490.         }
  491.  
  492.     //
  493.     // Return back to the EnumerateDirectoryTree function so that it
  494.     // can call us with the next file or directrry.
  495.     //
  496.  
  497.     return;
  498. }
  499.  
  500.  
  501.  
  502. VOID
  503. ProcessRequest(
  504.     IN PWORK_QUEUE_ITEM WorkItem
  505.     )
  506.  
  507. /*++
  508.  
  509. Routine Description:
  510.  
  511.     This function is called whenever a work item is removed from
  512.     the work queue by one of the worker threads.  Which worker
  513.     thread context this function is called in is arbitrary.
  514.  
  515.     This functions keeps a pointer to state information in
  516.     thread local storage.
  517.  
  518.     This function is called once at the beginning with a
  519.     special initialization call.  During this call, this
  520.     function allocates space for state information and
  521.     remembers the pointer to the state information in
  522.     a Thread Local Storage (TLS) slot.
  523.  
  524.     This function is called once at the end with a special
  525.     termination call.  During this call, this function
  526.     frees the state information allocated during the
  527.     initialization call.
  528.  
  529.     In between these two calls are zero or more calls to
  530.     handle a work item.  The work item is a search request
  531.     which is handled by the ProcessSearchFile function.
  532.  
  533. Arguments:
  534.  
  535.     WorkItem - Supplies a pointer to the work item just removed
  536.         from the work queue.  It is the responsibility of this
  537.         routine to free the memory used to hold the work item.
  538.  
  539. Return Value:
  540.  
  541.     None.
  542.  
  543. --*/
  544.  
  545. {
  546.     DWORD BytesWritten;
  547.     PSEARCH_REQUEST_STATE State;
  548.     PSEARCH_REQUEST SearchRequest;
  549.     CHAR MessageBuffer[ 2 * MAX_PATH ];
  550.  
  551.     if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
  552.         //
  553.         // First time initialization call.  Allocate space for
  554.         // state information.
  555.         //
  556.  
  557.         State = LocalAlloc( LMEM_ZEROINIT,
  558.                             sizeof( *State )
  559.                           );
  560.  
  561.         if (State != NULL) {
  562.             //
  563.             // Now create a virtual buffer, with an initial commitment
  564.             // of zero and a maximum commitment of 128KB.  This buffer
  565.             // will be used to accumulate the matched strings output
  566.             // during the search of a single file.  This is so the
  567.             // output can be written to standard output with a single
  568.             // write call, thus insuring that it remains contiguous
  569.             // in the output stream, and is not intermingled with the
  570.             // output of the other worker threads.
  571.             //
  572.  
  573.             if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
  574.                 //
  575.                 // The CurrentOutput field of the state block is
  576.                 // a pointer to where the next output goes in the
  577.                 // buffer.  It is initialized here and reset each
  578.                 // time the buffer is flushed to standard output.
  579.                 //
  580.  
  581.                 State->CurrentOutput = State->Buffer.Base;
  582.                 }
  583.             else {
  584.                 LocalFree( State );
  585.                 State = NULL;
  586.                 }
  587.             }
  588.  
  589.         //
  590.         // Remember the pointer to the state informaiton
  591.         // thread local storage.
  592.         //
  593.  
  594.         TlsSetValue( TlsIndex, State );
  595.         return;
  596.         }
  597.  
  598.     //
  599.     // Here to handle a work item or special terminate call.
  600.     // Get the state pointer from thread local storage.
  601.     //
  602.  
  603.     State = (PSEARCH_REQUEST_STATE)TlsGetValue( TlsIndex );
  604.     if (State == NULL) {
  605.         return;
  606.         }
  607.  
  608.     //
  609.     // If this is the special terminate work item, free the virtual
  610.     // buffer and state block allocated above and set the thread
  611.     // local storage value to NULL.  Return to caller.
  612.     //
  613.  
  614.     if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
  615.         FreeVirtualBuffer( &State->Buffer );
  616.         LocalFree( State );
  617.         TlsSetValue( TlsIndex, NULL );
  618.         return;
  619.         }
  620.  
  621.     //
  622.     // If not an initialize or terminate work item, then must be a
  623.     // search request.  Calculate the address of the search request
  624.     // block, based on the position of the WorkItem field in the
  625.     // SEARCH_REQUEST structure.
  626.     //
  627.  
  628.     SearchRequest = CONTAINING_RECORD( WorkItem, SEARCH_REQUEST, WorkItem );
  629.  
  630.     //
  631.     // Actual search operation is protected by a try ... except
  632.     // block so that any attempts to store into the virtual buffer
  633.     // will be handled correctly by extending the virtual buffer.
  634.     //
  635.  
  636.     _try {
  637.         //
  638.         // Perform the search against this file.
  639.         //
  640.  
  641.         ProcessSearchFile( SearchRequest, State );
  642.  
  643.         //
  644.         // Done with this file.  If using asynchronous I/O, decrement the
  645.         // count of outstanding I/O operations and it if goes to zero,
  646.         // then signal the IoCompletedEvent as there are no more outstanding
  647.         // I/O operations.
  648.         //
  649.  
  650.         if (ASyncIO && InterlockedDecrement( &OutstandingIOOperations ) == 0) {
  651.             SetEvent( IoCompletedEvent );
  652.             }
  653.  
  654.         //
  655.         // If any output was written to the virtual buffer,
  656.         // flush the output to standard output.  Trim the
  657.         // virtual buffer back to zero committed pages.
  658.         //
  659.  
  660.         if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
  661.             WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
  662.                        State->Buffer.Base,
  663.                        State->CurrentOutput - (LPSTR)State->Buffer.Base,
  664.                        &BytesWritten,
  665.                        NULL
  666.                      );
  667.  
  668.             TrimVirtualBuffer( &State->Buffer );
  669.             State->CurrentOutput = (LPSTR)State->Buffer.Base;
  670.             }
  671.         }
  672.  
  673.     _except( VirtualBufferExceptionFilter( GetExceptionCode(),
  674.                                           GetExceptionInformation(),
  675.                                           &State->Buffer
  676.                                         )
  677.           ) {
  678.  
  679.         //
  680.         // We will get here if the exception filter was unable to
  681.         // commit the memory.
  682.         //
  683.  
  684.         WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
  685.                    MessageBuffer,
  686.                    sprintf( MessageBuffer,
  687.                             "%s(0) : error 0: too many matches for file\n",
  688.                             SearchRequest->FullPathName
  689.                           ),
  690.                    &BytesWritten,
  691.                    NULL
  692.                  );
  693.         }
  694.  
  695.     //
  696.     // Free the storage used by the SearchRequest
  697.     //
  698.  
  699.     LocalFree( SearchRequest );
  700.  
  701.     //
  702.     // All done with this request.  Return to the worker thread that
  703.     // called us.
  704.     //
  705.  
  706.     return;
  707. }
  708.  
  709.  
  710.  
  711. VOID
  712. ProcessReadFileCompletion(
  713.     DWORD dwErrorCode,
  714.     DWORD dwNumberOfBytesTransfered,
  715.     LPOVERLAPPED lpOverlapped
  716.     )
  717.  
  718. /*++
  719.  
  720. Routine Description:
  721.  
  722.     This function is called whenever an asynchronous I/O operation,
  723.     queued by the previous function, completes.  This function
  724.     calulates the address of the search request block and then
  725.     queue the request to the work queue, now that the data is
  726.     in memory.
  727.  
  728. Arguments:
  729.  
  730.     dwErrorCode - Supplies the error code that the I/O completed with.
  731.  
  732.     dwNumberOfBytesTransfered - Supplies the actual number of bytes
  733.         transferred.
  734.  
  735.     lpOverlapped - Supplies a pointer to the structure given to
  736.         ReadFileEx when the I/O operation was queued.
  737.  
  738. Return Value:
  739.  
  740.     None.
  741.  
  742. --*/
  743.  
  744. {
  745.     PSEARCH_REQUEST SearchRequest;
  746.  
  747.     //
  748.     // Since the data we need is now in memory, queue the search
  749.     // request to the work queue.
  750.     //
  751.  
  752.     SearchRequest = CONTAINING_RECORD( lpOverlapped, SEARCH_REQUEST, OverlappedIO );
  753.     QueueWorkItem( SearchRequest->WorkItem.WorkQueue, &SearchRequest->WorkItem );
  754. }
  755.  
  756.  
  757.  
  758. VOID
  759. ProcessSearchFile(
  760.     IN PSEARCH_REQUEST SearchRequest,
  761.     IN PSEARCH_REQUEST_STATE State
  762.     )
  763.  
  764. /*++
  765.  
  766. Routine Description:
  767.  
  768.     This function performs the actual search of the contents of the
  769.     passed file for the search string given on the command line.
  770.     If we are using synchronous I/O, then do the read operation
  771.     now.
  772.  
  773.     Search the contents of the file for any matches, and accumulate
  774.     the match output in the virtual buffer using sprintf, which is
  775.     multi-thread safe, even with the single threaded version of
  776.     the libraries.
  777.  
  778. Arguments:
  779.  
  780.     SearchRequest - Supplies a pointer to the search request which
  781.         contains the relevant information.
  782.  
  783.     State - Supplies a pointer to state information for the current
  784.         thread.
  785.  
  786. Return Value:
  787.  
  788.     None.
  789.  
  790. --*/
  791.  
  792. {
  793.     LPSTR FileData, s, s1, BegLine, EndLine, EndOfFile;
  794.     DWORD LineNumber;
  795.     DWORD MatchesFound;
  796.     DWORD BytesRead;
  797.  
  798.     // Get a pointer to the beginning of the file data in memory
  799.     // and calculate the address of the end of file point in
  800.     // memory.
  801.     //
  802.  
  803.     FileData = SearchRequest->FileData;
  804.     EndOfFile = FileData + SearchRequest->FileSize;
  805.  
  806.     //
  807.     // If using synchronous I/O, then we have not read in the
  808.     // file contents yet, so issue the synchronous read to get
  809.     // the data into memory.
  810.     //
  811.  
  812.     if (SyncIO) {
  813.         if (!ReadFile( SearchRequest->File,
  814.                        FileData,
  815.                        SearchRequest->FileSize,
  816.                        &BytesRead,
  817.                        NULL
  818.                      ) ||
  819.             BytesRead != SearchRequest->FileSize
  820.            ) {
  821.             State->CurrentOutput += sprintf( State->CurrentOutput,
  822.                                              "%s(0) : error %u: Unable to read file contents.\n",
  823.                                              SearchRequest->FullPathName,
  824.                                              GetLastError()
  825.                                            );
  826.  
  827.             CloseHandle( SearchRequest->File );
  828.             return;
  829.             }
  830.         }
  831.  
  832.     //
  833.     // Close any open file handle associated with this request.
  834.     //
  835.  
  836.     if (SearchRequest->File != NULL) {
  837.         CloseHandle( SearchRequest->File );
  838.         }
  839.  
  840.     //
  841.     // Search the file contents, keeping track of line breaks
  842.     // so we can tell the line number of each match.
  843.     //
  844.  
  845.     s = FileData;
  846.     LineNumber = 0;
  847.     MatchesFound = 0;
  848.     while (s < EndOfFile) {
  849.         BegLine = s;
  850.         while (s < EndOfFile && *s != '\n') {
  851.             s++;
  852.             }
  853.  
  854.         if (*s == '\n') {
  855.             LineNumber += 1;
  856.             EndLine = s - 1;
  857.             if (EndLine > BegLine && EndLine[ -1 ] == '\r') {
  858.                 EndLine -= 1;
  859.                 }
  860.  
  861.             s1 = BegLine;
  862.             while (s1 < (EndLine - SearchStringLength)) {
  863.                 if (!(SearchFunction)( s1, SearchString, SearchStringLength )) {
  864.                     //
  865.                     // We have a match for this line.  Append the
  866.                     // output to the virtual buffer and update the
  867.                     // current output pointer.
  868.                     //
  869.  
  870.                     State->CurrentOutput += sprintf( State->CurrentOutput,
  871.                                                      "%s(%u) : %.*s\n",
  872.                                                      SearchRequest->FullPathName,
  873.                                                      LineNumber,
  874.                                                      EndLine - BegLine,
  875.                                                      BegLine
  876.                                                    );
  877.                     MatchesFound += 1;
  878.                     break;
  879.                     }
  880.  
  881.                 s1++;
  882.                 }
  883.  
  884.             s++;
  885.             }
  886.         }
  887.  
  888.     if (MatchesFound) {
  889.         MatchedLineCount += MatchesFound;
  890.         MatchedFileCount += 1;
  891.         }
  892.     SearchedFileCount += 1;
  893.  
  894.     //
  895.     // All done with the file contents.  Discard it either by
  896.     // unmapping the view of the file in the case of mapped file
  897.     // I/O or free the virtual memory for other types of I/O
  898.     //
  899.  
  900.     if (MappedFileIO) {
  901.         if (!UnmapViewOfFile( FileData )) {
  902.             State->CurrentOutput += sprintf( State->CurrentOutput,
  903.                                              "%s(%u) : UnmapViewOfFile( %08x ) failed, error == %u\n",
  904.                                              SearchRequest->FullPathName,
  905.                                              LineNumber,
  906.                                              FileData,
  907.                                              GetLastError()
  908.                                            );
  909.             }
  910.         }
  911.     else {
  912.         VirtualFree( FileData, 0, MEM_RELEASE );
  913.         }
  914. }
  915.  
  916.  
  917. PWORK_QUEUE
  918. CreateWorkQueue(
  919.     IN DWORD NumberOfWorkerThreads,
  920.     IN PWORKER_ROUTINE WorkerRoutine
  921.     )
  922.  
  923. /*++
  924.  
  925. Routine Description:
  926.  
  927.     This function creates a work queue, with the specified number of
  928.     threads to service work items placed in the queue.  Work items
  929.     are removed from the queue in the same order that they are placed
  930.     in the queue.
  931.  
  932. Arguments:
  933.  
  934.     NumberOfWorkerThreads - Specifies how many threads this function
  935.         should create to process work items placed in the queue.
  936.         Must be greater than 0 and less than 128.
  937.  
  938.     WorkerRoutine - Specifies the address of a routine to call
  939.         for each work item as it is removed from the queue.  The
  940.         thread context the routine is called in is undefined.
  941.  
  942. Return Value:
  943.  
  944.     A pointer to the work queue.  Returns NULL if unable to create
  945.     the work queue and its worker threads.  Extended error information
  946.     is available from GetLastError()
  947.  
  948. --*/
  949.  
  950. {
  951.     PWORK_QUEUE WorkQueue;
  952.     HANDLE Thread;
  953.     DWORD ThreadId;
  954.     DWORD i;
  955.  
  956.     //
  957.     // Allocate space for the work queue, which includes an
  958.     // array of thread handles.
  959.     //
  960.  
  961.     WorkQueue = LocalAlloc( LMEM_ZEROINIT,
  962.                             sizeof( *WorkQueue ) +
  963.                                 (NumberOfWorkerThreads * sizeof( HANDLE ))
  964.                           );
  965.     if (WorkQueue == NULL) {
  966.         return NULL;
  967.         }
  968.  
  969.     //
  970.     // The work queue is controlled by a counting semaphore that
  971.     // is incremented each time a work item is placed in the queue
  972.     // and decremented each time a worker thread wakes up to remove
  973.     // an item from the queue.
  974.     //
  975.  
  976.     if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 100000, NULL )) {
  977.         //
  978.         // Mutual exclusion between the worker threads accessing
  979.         // the work queue is done with a critical section.
  980.         //
  981.  
  982.         InitializeCriticalSection( &WorkQueue->CriticalSection );
  983.  
  984.         //
  985.         // The queue itself is just a doubly linked list, where
  986.         // items are placed in the queue at the tail of the list
  987.         // and removed from the queue from the head of the list.
  988.         //
  989.  
  990.         InitializeListHead( &WorkQueue->Queue );
  991.  
  992.         //
  993.         // Removed the address of the supplied worker function
  994.         // in the work queue structure.
  995.         //
  996.  
  997.         WorkQueue->WorkerRoutine = WorkerRoutine;
  998.  
  999.         //
  1000.         // Now create the requested number of worker threads.
  1001.         // The handle to each thread is remembered in an
  1002.         // array of thread handles in the work queue structure.
  1003.         //
  1004.  
  1005.         for (i=0; i<NumberOfWorkerThreads; i++) {
  1006.             Thread = CreateThread( NULL,
  1007.                                    0,
  1008.                                    (LPTHREAD_START_ROUTINE) WorkerThread,
  1009.                                    WorkQueue,
  1010.                                    0,
  1011.                                    &ThreadId
  1012.                                  );
  1013.             if (Thread == NULL) {
  1014.                 break;
  1015.                 }
  1016.             else {
  1017.                 WorkQueue->NumberOfWorkerThreads++;
  1018.                 WorkQueue->WorkerThreads[ i ] = Thread;
  1019.                 SetThreadPriority( Thread, THREAD_PRIORITY_ABOVE_NORMAL );
  1020.                 }
  1021.             }
  1022.  
  1023.         //
  1024.         // If we successfully created all of the worker threads
  1025.         // then return the address of the work queue structure
  1026.         // to indicate success.
  1027.         //
  1028.  
  1029.         if (i == NumberOfWorkerThreads) {
  1030.             return WorkQueue;
  1031.             }
  1032.         }
  1033.  
  1034.     //
  1035.     // Failed for some reason.  Destroy whatever we managed
  1036.     // to create and return failure to the caller.
  1037.     //
  1038.  
  1039.     DestroyWorkQueue( WorkQueue );
  1040.     return NULL;
  1041. }
  1042.  
  1043.  
  1044.  
  1045. VOID
  1046. DestroyWorkQueue(
  1047.     IN OUT PWORK_QUEUE WorkQueue
  1048.     )
  1049.  
  1050. /*++
  1051.  
  1052. Routine Description:
  1053.  
  1054.     This function destroys a work queue created with the CreateWorkQueue
  1055.     functions.  It attempts to shut down the worker threads cleanly
  1056.     by queueing a terminate work item to each worker thread.  It then
  1057.     waits for all the worker threads to terminate.  If the wait is
  1058.     not satisfied within 30 seconds, then it goes ahead and terminates
  1059.     all of the worker threads.
  1060.  
  1061. Arguments:
  1062.  
  1063.     WorkQueue - Supplies a pointer to the work queue to destroy.
  1064.  
  1065. Return Value:
  1066.  
  1067.     None.
  1068.  
  1069. --*/
  1070.  
  1071. {
  1072.     DWORD i;
  1073.     DWORD rc;
  1074.  
  1075.     //
  1076.     // If the semaphore handle field is not NULL, then there
  1077.     // may be threads to terminate.
  1078.     //
  1079.  
  1080.     if (WorkQueue->Semaphore != NULL) {
  1081.         //
  1082.         // Set the termiating flag in the work queue and
  1083.         // signal the counting semaphore by the number
  1084.         // worker threads so they will all wake up and
  1085.         // notice the terminating flag and exit.
  1086.         //
  1087.  
  1088.         EnterCriticalSection( &WorkQueue->CriticalSection );
  1089.         _try {
  1090.             WorkQueue->Terminating = TRUE;
  1091.             ReleaseSemaphore( WorkQueue->Semaphore,
  1092.                               WorkQueue->NumberOfWorkerThreads,
  1093.                               NULL
  1094.                             );
  1095.             }
  1096.         _finally {
  1097.             LeaveCriticalSection( &WorkQueue->CriticalSection );
  1098.             }
  1099.  
  1100.         //
  1101.         // Wait for all worker threads to wake up and see the
  1102.         // terminate flag and then terminate themselves.  Timeout
  1103.         // the wait after 30 seconds.
  1104.         //
  1105.  
  1106.         while (TRUE) {
  1107.             rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
  1108.                                            WorkQueue->WorkerThreads,
  1109.                                            TRUE,
  1110.                                            30000,
  1111.                                            TRUE
  1112.                                          );
  1113.             if (rc == WAIT_IO_COMPLETION) {
  1114.                 //
  1115.                 // If we came out of the wait because an I/O
  1116.                 // completion routine was called, reissue the
  1117.                 // wait.
  1118.                 //
  1119.                 continue;
  1120.                 }
  1121.             else {
  1122.                 break;
  1123.                 }
  1124.             }
  1125.  
  1126.         //
  1127.         // Now close our thread handles so they will actually
  1128.         // evaporate.  If the wait above was unsuccessful,
  1129.         // then first attempt to force the termination of
  1130.         // each worker thread prior to closing the handle.
  1131.         //
  1132.  
  1133.         for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
  1134.             if (rc != NO_ERROR) {
  1135.                 TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
  1136.                 }
  1137.  
  1138.             CloseHandle( WorkQueue->WorkerThreads[ i ] );
  1139.             }
  1140.  
  1141.         //
  1142.         // All threads stopped, all thread handles closed.  Now
  1143.         // delete the critical section and close the semaphore
  1144.         // handle.
  1145.         //
  1146.  
  1147.         DeleteCriticalSection( &WorkQueue->CriticalSection );
  1148.         CloseHandle( WorkQueue->Semaphore );
  1149.         }
  1150.  
  1151.     //
  1152.     // Everything done, now free the memory used by the work queue.
  1153.     //
  1154.  
  1155.     LocalFree( WorkQueue );
  1156.     return;
  1157. }
  1158.  
  1159.  
  1160.  
  1161. BOOL
  1162. QueueWorkItem(
  1163.     IN OUT PWORK_QUEUE WorkQueue,
  1164.     IN PWORK_QUEUE_ITEM WorkItem
  1165.     )
  1166.  
  1167. /*++
  1168.  
  1169. Routine Description:
  1170.  
  1171.     This function queues a work item to the passed work queue that is
  1172.     processed by one of the worker threads associated with the queue.
  1173.  
  1174. Arguments:
  1175.  
  1176.     WorkQueue - Supplies a pointer to the work queue that is to
  1177.         receive the work item.
  1178.  
  1179.     WorkItem - Supplies a pointer to the work item to add the the queue.
  1180.         The work item structure contains a doubly linked list entry, the
  1181.         address of a routine to call and a parameter to pass to that
  1182.         routine.  It is the routine's responsibility to reclaim the
  1183.         storage occupied by the WorkItem structure.
  1184.  
  1185. Return Value:
  1186.  
  1187.     TRUE if operation was successful.  Otherwise returns FALSE and
  1188.     extended error information is available from GetLastError()
  1189.  
  1190. --*/
  1191.  
  1192. {
  1193.     BOOL Result;
  1194.  
  1195.     //
  1196.     // Acquire the work queue critical section and insert the work item
  1197.     // in the queue and release the semaphore if the work item is not
  1198.     // already in the list.
  1199.     //
  1200.  
  1201.     EnterCriticalSection( &WorkQueue->CriticalSection );
  1202.     Result = TRUE;
  1203.     _try {
  1204.         WorkItem->WorkQueue = WorkQueue;
  1205.         InsertTailList( &WorkQueue->Queue, &WorkItem->List );
  1206.         Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
  1207.         }
  1208.     _finally {
  1209.         LeaveCriticalSection( &WorkQueue->CriticalSection );
  1210.         }
  1211.  
  1212.     return Result;
  1213. }
  1214.  
  1215.  
  1216.  
  1217. DWORD
  1218. WorkerThread(
  1219.     LPVOID lpThreadParameter
  1220.     )
  1221. {
  1222.     PWORK_QUEUE WorkQueue = (PWORK_QUEUE)lpThreadParameter;
  1223.     DWORD rc;
  1224.     WORK_QUEUE_ITEM InitWorkItem;
  1225.     PWORK_QUEUE_ITEM WorkItem;
  1226.  
  1227.     //
  1228.     // Call the worker routine with an initialize work item
  1229.     // to give it a change to initialize some per thread
  1230.     // state that will passed to it for each subsequent
  1231.     // work item.
  1232.     //
  1233.  
  1234.     InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
  1235.     (WorkQueue->WorkerRoutine)( &InitWorkItem );
  1236.     while( TRUE ) {
  1237.         _try {
  1238.  
  1239.             //
  1240.             // Wait until something is put in the queue (semaphore is
  1241.             // released), remove the item from the queue, mark it not
  1242.             // inserted, and execute the specified routine.
  1243.             //
  1244.  
  1245.             rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
  1246.             if (rc == WAIT_IO_COMPLETION) {
  1247.                 continue;
  1248.                 }
  1249.  
  1250.             EnterCriticalSection( &WorkQueue->CriticalSection );
  1251.             _try {
  1252.                 if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
  1253.                     break;
  1254.                     }
  1255.  
  1256.                 WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
  1257.                 }
  1258.             _finally {
  1259.                 LeaveCriticalSection( &WorkQueue->CriticalSection );
  1260.                 }
  1261.  
  1262.             //
  1263.             // Execute the worker routine for this work item.
  1264.             //
  1265.  
  1266.             (WorkQueue->WorkerRoutine)( WorkItem );
  1267.             }
  1268.         _except( EXCEPTION_EXECUTE_HANDLER ) {
  1269.             //
  1270.             // Ignore any exceptions from worker routine.
  1271.             //
  1272.             }
  1273.         }
  1274.  
  1275.     InitWorkItem.Reason = WORK_TERMINATE_ITEM;
  1276.     (WorkQueue->WorkerRoutine)( &InitWorkItem );
  1277.  
  1278.     ExitThread( 0 );
  1279.     return 0;       // This will exit this thread
  1280. }
  1281.  
  1282.  
  1283. BOOL
  1284. CreateVirtualBuffer(
  1285.     OUT PVIRTUAL_BUFFER Buffer,
  1286.     IN DWORD CommitSize,
  1287.     IN DWORD ReserveSize OPTIONAL
  1288.     )
  1289.  
  1290. /*++
  1291.  
  1292. Routine Description:
  1293.  
  1294.     This function is called to create a virtual buffer.  A virtual
  1295.     buffer is a contiguous range of virtual memory, where some initial
  1296.     prefix portion of the memory is committed and the remainder is only
  1297.     reserved virtual address space.  A routine is provided to extend the
  1298.     size of the committed region incrementally or to trim the size of
  1299.     the committed region back to some specified amount.
  1300.  
  1301. Arguments:
  1302.  
  1303.     Buffer - Pointer to the virtual buffer control structure that is
  1304.         filled in by this function.
  1305.  
  1306.     CommitSize - Size of the initial committed portion of the buffer.
  1307.         May be zero.
  1308.  
  1309.     ReserveSize - Amount of virtual address space to reserve for the
  1310.         buffer.  May be zero, in which case amount reserved is the
  1311.         committed size plus one, rounded up to the next 64KB boundary.
  1312.  
  1313. Return Value:
  1314.  
  1315.     TRUE if operation was successful.  Otherwise returns FALSE and
  1316.     extended error information is available from GetLastError()
  1317.  
  1318. --*/
  1319.  
  1320. {
  1321.     SYSTEM_INFO SystemInformation;
  1322.  
  1323.     //
  1324.     // Query the page size from the system for rounding
  1325.     // our memory allocations.
  1326.     //
  1327.  
  1328.     GetSystemInfo( &SystemInformation );
  1329.     Buffer->PageSize = SystemInformation.dwPageSize;
  1330.  
  1331.     //
  1332.     // If the reserve size was not specified, default it by
  1333.     // rounding up the initial committed size to a 64KB
  1334.     // boundary.  This is because the Win32 Virtual Memory
  1335.     // API calls always allocate virtual address space on
  1336.     // 64KB boundaries, so we might well have it available
  1337.     // for commitment.
  1338.     //
  1339.  
  1340.     if (!ARGUMENT_PRESENT( ReserveSize )) {
  1341.         ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
  1342.         }
  1343.  
  1344.     //
  1345.     // Attempt to reserve the address space.
  1346.     //
  1347.  
  1348.     Buffer->Base = VirtualAlloc( NULL,
  1349.                                  ReserveSize,
  1350.                                  MEM_RESERVE,
  1351.                                  PAGE_READWRITE
  1352.                                );
  1353.     if (Buffer->Base == NULL) {
  1354.         //
  1355.         // Unable to reserve address space, return failure.
  1356.         //
  1357.  
  1358.         return FALSE;
  1359.         }
  1360.  
  1361.     //
  1362.     // Attempt to commit some initial portion of the reserved region.
  1363.     //
  1364.     //
  1365.  
  1366.     CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
  1367.     if (CommitSize == 0 ||
  1368.         VirtualAlloc( Buffer->Base,
  1369.                       CommitSize,
  1370.                       MEM_COMMIT,
  1371.                       PAGE_READWRITE
  1372.                     ) != NULL
  1373.        ) {
  1374.         //
  1375.         // Either the size of the committed region was zero or the
  1376.         // commitment succeeded.  In either case calculate the
  1377.         // address of the first byte after the committed region
  1378.         // and the address of the first byte after the reserved
  1379.         // region and return successs.
  1380.         //
  1381.  
  1382.         Buffer->CommitLimit = (LPVOID)
  1383.             ((char *)Buffer->Base + CommitSize);
  1384.  
  1385.         Buffer->ReserveLimit = (LPVOID)
  1386.             ((char *)Buffer->Base + ReserveSize);
  1387.  
  1388.         return TRUE;
  1389.         }
  1390.  
  1391.     //
  1392.     // If unable to commit the memory, release the virtual address
  1393.     // range allocated above and return failure.
  1394.     //
  1395.  
  1396.     VirtualFree( Buffer->Base, 0, MEM_RELEASE );
  1397.     return FALSE;
  1398. }
  1399.  
  1400.  
  1401.  
  1402. BOOL
  1403. ExtendVirtualBuffer(
  1404.     IN PVIRTUAL_BUFFER Buffer,
  1405.     IN LPVOID Address
  1406.     )
  1407.  
  1408. /*++
  1409.  
  1410. Routine Description:
  1411.  
  1412.     This function is called to extend the committed portion of a virtual
  1413.     buffer.
  1414.  
  1415. Arguments:
  1416.  
  1417.     Buffer - Pointer to the virtual buffer control structure.
  1418.  
  1419.     Address - Byte at this address is committed, along with all memory
  1420.         from the beginning of the buffer to this address.  If the
  1421.         address is already within the committed portion of the virtual
  1422.         buffer, then this routine does nothing.  If outside the reserved
  1423.         portion of the virtual buffer, then this routine returns an
  1424.         error.
  1425.  
  1426.         Otherwise enough pages are committed so that the memory from the
  1427.         base of the buffer to the passed address is a contiguous region
  1428.         of committed memory.
  1429.  
  1430.  
  1431. Return Value:
  1432.  
  1433.     TRUE if operation was successful.  Otherwise returns FALSE and
  1434.     extended error information is available from GetLastError()
  1435.  
  1436. --*/
  1437.  
  1438. {
  1439.     DWORD NewCommitSize;
  1440.     LPVOID NewCommitLimit;
  1441.  
  1442.     //
  1443.     // See if address is within the buffer.
  1444.     //
  1445.  
  1446.     if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
  1447.         //
  1448.         // See if the address is within the committed portion of
  1449.         // the buffer.  If so return success immediately.
  1450.         //
  1451.  
  1452.         if (Address < Buffer->CommitLimit) {
  1453.             return TRUE;
  1454.             }
  1455.  
  1456.         //
  1457.         // Address is within the reserved portion.  Determine how many
  1458.         // bytes are between the address and the end of the committed
  1459.         // portion of the buffer.  Round this size to a multiple of
  1460.         // the page size and this is the amount we will attempt to
  1461.         // commit.
  1462.         //
  1463.  
  1464.         NewCommitSize =
  1465.             ((DWORD)ROUND_UP( (DWORD)Address + 1, Buffer->PageSize ) -
  1466.              (DWORD)Buffer->CommitLimit
  1467.             );
  1468.  
  1469.         //
  1470.         // Attempt to commit the memory.
  1471.         //
  1472.  
  1473.         NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
  1474.                                        NewCommitSize,
  1475.                                        MEM_COMMIT,
  1476.                                        PAGE_READWRITE
  1477.                                      );
  1478.         if (NewCommitLimit != NULL) {
  1479.             //
  1480.             // Successful, so update the upper limit of the committed
  1481.             // region of the buffer and return success.
  1482.             //
  1483.  
  1484.             Buffer->CommitLimit = (LPVOID)
  1485.                 ((DWORD)NewCommitLimit + NewCommitSize);
  1486.  
  1487.             return TRUE;
  1488.             }
  1489.         }
  1490.  
  1491.     //
  1492.     // Address is outside of the buffer, return failure.
  1493.     //
  1494.  
  1495.     return FALSE;
  1496. }
  1497.  
  1498.  
  1499. BOOL
  1500. TrimVirtualBuffer(
  1501.     IN PVIRTUAL_BUFFER Buffer
  1502.     )
  1503.  
  1504. /*++
  1505.  
  1506. Routine Description:
  1507.  
  1508.     This function is called to decommit any memory that has been
  1509.     committed for this virtual buffer.
  1510.  
  1511. Arguments:
  1512.  
  1513.     Buffer - Pointer to the virtual buffer control structure.
  1514.  
  1515. Return Value:
  1516.  
  1517.     TRUE if operation was successful.  Otherwise returns FALSE and
  1518.     extended error information is available from GetLastError()
  1519.  
  1520. --*/
  1521.  
  1522. {
  1523.     Buffer->CommitLimit = Buffer->Base;
  1524.     return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
  1525. }
  1526.  
  1527.  
  1528.  
  1529. BOOL
  1530. FreeVirtualBuffer(
  1531.     IN PVIRTUAL_BUFFER Buffer
  1532.     )
  1533. /*++
  1534.  
  1535. Routine Description:
  1536.  
  1537.     This function is called to free all the memory that is associated
  1538.     with this virtual buffer.
  1539.  
  1540. Arguments:
  1541.  
  1542.     Buffer - Pointer to the virtual buffer control structure.
  1543.  
  1544. Return Value:
  1545.  
  1546.     TRUE if operation was successful.  Otherwise returns FALSE and
  1547.     extended error information is available from GetLastError()
  1548.  
  1549. --*/
  1550.  
  1551. {
  1552.     //
  1553.     // Decommit and release all virtual memory associated with
  1554.     // this virtual buffer.
  1555.     //
  1556.  
  1557.     return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
  1558. }
  1559.  
  1560.  
  1561.  
  1562. int
  1563. VirtualBufferExceptionFilter(
  1564.     IN DWORD ExceptionCode,
  1565.     IN PEXCEPTION_POINTERS ExceptionInfo,
  1566.     IN OUT PVIRTUAL_BUFFER Buffer
  1567.     )
  1568.  
  1569. /*++
  1570.  
  1571. Routine Description:
  1572.  
  1573.     This function is an exception filter that handles exceptions that
  1574.     referenced uncommitted but reserved memory contained in the passed
  1575.     virtual buffer.  It this filter routine is able to commit the
  1576.     additional pages needed to allow the memory reference to succeed,
  1577.     then it will re-execute the faulting instruction.  If it is unable
  1578.     to commit the pages, it will execute the callers exception handler.
  1579.  
  1580.     If the exception is not an access violation or is an access
  1581.     violation but does not reference memory contained in the reserved
  1582.     portion of the virtual buffer, then this filter passes the exception
  1583.     on up the exception chain.
  1584.  
  1585. Arguments:
  1586.  
  1587.     ExceptionCode - Reason for the exception.
  1588.  
  1589.     ExceptionInfo - Information about the exception and the context
  1590.         that it occurred in.
  1591.  
  1592.     Buffer - Points to a virtual buffer control structure that defines
  1593.         the reserved memory region that is to be committed whenever an
  1594.         attempt is made to access it.
  1595.  
  1596. Return Value:
  1597.  
  1598.     Exception disposition code that tells the exception dispatcher what
  1599.     to do with this exception.  One of three values is returned:
  1600.  
  1601.         EXCEPTION_EXECUTE_HANDLER - execute the exception handler
  1602.             associated with the exception clause that called this filter
  1603.             procedure.
  1604.  
  1605.         EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
  1606.             handler to handle this exception.
  1607.  
  1608.         EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
  1609.             control to the instruction that caused the exception.
  1610.  
  1611.  
  1612. --*/
  1613.  
  1614. {
  1615.     LPVOID FaultingAddress;
  1616.  
  1617.     //
  1618.     // If this is an access violation touching memory within
  1619.     // our reserved buffer, but outside of the committed portion
  1620.     // of the buffer, then we are going to take this exception.
  1621.     //
  1622.  
  1623.     if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
  1624.         //
  1625.         // Get the virtual address that caused the access violation
  1626.         // from the exception record.  Determine if the address
  1627.         // references memory within the reserved but uncommitted
  1628.         // portion of the virtual buffer.
  1629.         //
  1630.  
  1631.         FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
  1632.         if (FaultingAddress >= Buffer->CommitLimit &&
  1633.             FaultingAddress <= Buffer->ReserveLimit
  1634.            ) {
  1635.             //
  1636.             // This is our exception.  Try to extend the buffer
  1637.             // to including the faulting address.
  1638.             //
  1639.  
  1640.             if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
  1641.                 //
  1642.                 // Buffer successfully extended, so re-execute the
  1643.                 // faulting instruction.
  1644.                 //
  1645.  
  1646.                 return EXCEPTION_CONTINUE_EXECUTION;
  1647.                 }
  1648.             else {
  1649.                 //
  1650.                 // Unable to extend the buffer.  Stop searching
  1651.                 // for exception handlers and execute the caller's
  1652.                 // handler.
  1653.                 //
  1654.  
  1655.                 return EXCEPTION_EXECUTE_HANDLER;
  1656.                 }
  1657.             }
  1658.         }
  1659.  
  1660.     //
  1661.     // Not an exception we care about, so pass it up the chain.
  1662.     //
  1663.  
  1664.     return EXCEPTION_CONTINUE_SEARCH;
  1665. }
  1666.  
  1667.  
  1668. BOOL
  1669. EnumerateDirectoryTree(
  1670.     LPSTR DirectoryPath,
  1671.     PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
  1672.     PVOID EnumerateParameter
  1673.     )
  1674.  
  1675. /*++
  1676.  
  1677. Routine Description:
  1678.  
  1679.     This function walks a directory tree, depth first, calling the
  1680.     passed enumeration routine for each directory and file found
  1681.     in the tree.  The enumeration routine is passed the full path
  1682.     of the file, the directory information associated with the file
  1683.     and an enumeration parameter that is uninterpreted by this
  1684.     function.
  1685.  
  1686. Arguments:
  1687.  
  1688.     DirectoryPath - Absolute or relative path to the directory that
  1689.         will is the root of the tree to enumerate.
  1690.  
  1691.     EnumerateRoutine - Pointer to an enumeration routine to call
  1692.         for each file and directory found.
  1693.  
  1694.     EnumerateParameter - Uninterpreted 32-bit value that is passed
  1695.         to the EnumerationRoutine each time it is called.
  1696.  
  1697. Return Value:
  1698.  
  1699.     TRUE if operation was successful.  Otherwise returns FALSE and
  1700.     extended error information is available from GetLastError()
  1701.  
  1702. --*/
  1703.  
  1704. {
  1705.     BOOL Result;
  1706.     VIRTUAL_BUFFER Buffer;
  1707.     PENUMERATE_DIRECTORY_STATE State;
  1708.     PENUMERATE_DIRECTORY_STACK Stack;
  1709.     WIN32_FIND_DATA FindFileData;
  1710.  
  1711.     //
  1712.     // Create a virtual buffer with an initial committed size of
  1713.     // our directory state buffer, and a maximum reserved size of
  1714.     // the longest possible full path based on the maximum depth
  1715.     // we handle and the maximum length of each path component.
  1716.     //
  1717.  
  1718.     if (!CreateVirtualBuffer( &Buffer,
  1719.                               sizeof( ENUMERATE_DIRECTORY_STATE ),
  1720.                               sizeof( ENUMERATE_DIRECTORY_STATE ) +
  1721.                                  MAX_DEPTH * MAX_PATH
  1722.                             )
  1723.        ) {
  1724.         return FALSE;
  1725.         }
  1726.  
  1727.     //
  1728.     // This buffer will be used to maintain a stack of directory
  1729.     // search handles, as well as accumulate the full path string
  1730.     // as we descend the directory tree.
  1731.     //
  1732.  
  1733.     State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
  1734.     State->Depth = 0;
  1735.     Stack = &State->Stack[ 0 ];
  1736.  
  1737.     //
  1738.     // Enter a try ... finally block so we can insure that we clean
  1739.     // up after ourselves on exit.
  1740.     //
  1741.  
  1742.     _try {
  1743.         //
  1744.         // First translate the passed in DirectoryPath into a fully
  1745.         // qualified path.  This path will be the initial value in
  1746.         // our path buffer.  The initial allocation of the path buffer
  1747.         // is big enough for this initial request, so does not need
  1748.         // to be guarded by a try ... except clause.
  1749.         //
  1750.  
  1751.         if (GetFullPathName( DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd )) {
  1752.             //
  1753.             // Now enter a try ... except block that will be used to
  1754.             // manage the commitment of space in the path buffer as
  1755.             // we append subdirectory names and file names to it.
  1756.             // Using the virtual buffer allows us to handle full
  1757.             // path names up to 16KB in length, with an initial
  1758.             // allocation of 4KB.
  1759.             //
  1760.  
  1761.             _try {
  1762.                 //
  1763.                 // Walk the directory tree.  The outer loop is executed
  1764.                 // once for each directory in the tree.
  1765.                 //
  1766.  
  1767.                 while (TRUE) {
  1768. startDirectorySearch:
  1769.                     //
  1770.                     // Find the end of the current path, and make sure
  1771.                     // there is a trailing path separator.
  1772.                     //
  1773.  
  1774.                     Stack->PathEnd = strchr( State->Path, '\0' );
  1775.                     if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
  1776.                         *(Stack->PathEnd)++ = '\\';
  1777.                         }
  1778.  
  1779.                     //
  1780.                     // Now append the wild card specification that will
  1781.                     // let us enumerate all the entries in this directory.
  1782.                     // Call FindFirstFile to find the first entry in the
  1783.                     // directory.
  1784.                     //
  1785.  
  1786.                     strcpy( Stack->PathEnd, "*.*" );
  1787.                     Stack->FindHandle = FindFirstFile( State->Path,
  1788.                                                        &FindFileData
  1789.                                                      );
  1790.                     if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
  1791.                         //
  1792.                         // Entry found.  Now loop through the entire
  1793.                         // directory processing each entry found,
  1794.                         // including the first one.
  1795.                         //
  1796.                         do {
  1797.                             //
  1798.                             // Ignore bogus pseudo-directories that are
  1799.                             // returned by some file systems (e.g. FAT).
  1800.                             //
  1801.  
  1802.                             if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
  1803.                                 (!strcmp( FindFileData.cFileName, "." ) ||
  1804.                                  !strcmp( FindFileData.cFileName, ".." )
  1805.                                 )
  1806.                                ) {
  1807.                                 continue;
  1808.                                 }
  1809.  
  1810.                             //
  1811.                             // Copy the file name portion from the current
  1812.                             // directory entry to the last component in the
  1813.                             // path buffer.
  1814.                             //
  1815.  
  1816.                             strcpy( Stack->PathEnd, FindFileData.cFileName );
  1817.  
  1818.                             //
  1819.                             // Call the supplied enumeration routine with the
  1820.                             // full path we have built up in the path buffer,
  1821.                             // the directory information for this directory
  1822.                             // entry and the supplied enumeration parameter.
  1823.                             //
  1824.  
  1825.                             (*EnumerateRoutine)( State->Path, &FindFileData, EnumerateParameter );
  1826.  
  1827.                             //
  1828.                             // If this is entry is a subdirectory, then it is
  1829.                             // time to recurse.  Do this by incrementing the
  1830.                             // stack pointer and depth and jumping to the top
  1831.                             // of the outer loop to process current contents
  1832.                             // of the path buffer as a fully qualified name of
  1833.                             // a directory.
  1834.                             //
  1835.  
  1836.                             if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  1837.                                 Stack++;
  1838.                                 State->Depth++;
  1839.                                 goto startDirectorySearch;
  1840. restartDirectorySearch:         ;
  1841.                                 }
  1842.  
  1843.                             //
  1844.                             // Here to find the next entry in the current directory.
  1845.                             //
  1846.                             }
  1847.  
  1848.                         while ( FindNextFile( Stack->FindHandle, &FindFileData ) );
  1849.  
  1850.                         //
  1851.                         // No more entries in the current directory, so close
  1852.                         // the search handle and fall into the code that will
  1853.                         // pop our stack of directory seacrh handles.
  1854.  
  1855.                         FindClose( Stack->FindHandle );
  1856.                         }
  1857.  
  1858.                     //
  1859.                     // Here when done with a directory.  See if we are pushed
  1860.                     // inside another directory.  If not, then we are done
  1861.                     // enumerating the whole tree, so break out of the loop.
  1862.                     //
  1863.  
  1864.                     if (!State->Depth) {
  1865.                         Result = TRUE;
  1866.                         break;
  1867.                         }
  1868.  
  1869.                     //
  1870.                     // We were pushed within another directory search,
  1871.                     // so pop the stack to restore its search handle
  1872.                     // and path buffer position and resume the search
  1873.                     // within that directory.
  1874.  
  1875.                     State->Depth--;
  1876.                     --Stack;
  1877.                     goto restartDirectorySearch;
  1878.                     }
  1879.                 }
  1880.  
  1881.             //
  1882.             // Any of the code that appends to the path buffer within
  1883.             // the above try ... except clause can cause an access
  1884.             // violation if the path buffer becomes longer than its
  1885.             // current committed size.  This exception filter
  1886.             // will dynamically commit additional pages as needed
  1887.             // and resume execution.
  1888.             //
  1889.  
  1890.             _except( VirtualBufferExceptionFilter( GetExceptionCode(),
  1891.                                                   GetExceptionInformation(),
  1892.                                                   &Buffer
  1893.                                                 )
  1894.                   ) {
  1895.                 //
  1896.                 // We will get here if the exception filter was unable to
  1897.                 // commit the memory.
  1898.                 //
  1899.  
  1900.                 Result = FALSE;
  1901.                 }
  1902.             }
  1903.         else {
  1904.             //
  1905.             // Initial GetFullPathName failed, so return a failure.
  1906.             //
  1907.  
  1908.             Result = FALSE;
  1909.             }
  1910.         }
  1911.     _finally {
  1912.         //
  1913.         // Here on our way out of the outer try ... finally block.
  1914.         // Make sure all our search handles have been closed and then
  1915.         // free the virtual buffer.  The only way this code is not
  1916.         // executed is if code within the try ... finally block
  1917.         // called ExitThread or ExitProcess, or an external thread
  1918.         // or process terminated this thread or process.
  1919.         //
  1920.         // In the case of process death, this is not a problem, because
  1921.         // process terminate closes all open handles attached to the process
  1922.         // and frees all private virtual memory that is part of the address
  1923.         // space of the process.
  1924.         //
  1925.         // In the case ot thread death, the code below is not executed if
  1926.         // the thread terminates via ExitThread in the context of the
  1927.         // try .. finally or if an external thread, either in this process
  1928.         // or another process called TerminateThread on this thread.
  1929.         //
  1930.  
  1931.         while (State->Depth--) {
  1932.             --Stack;
  1933.             FindClose( Stack->FindHandle );
  1934.             }
  1935.  
  1936.         FreeVirtualBuffer( &Buffer );
  1937.         }
  1938.  
  1939.     return Result;
  1940. }
  1941.  
  1942.  
  1943. BOOL
  1944. ProcessCommandLineArguments(
  1945.     int argc,
  1946.     char *argv[]
  1947.     )
  1948. {
  1949.     BOOL Result;
  1950.     LPSTR s;
  1951.  
  1952.     Result = FALSE;
  1953.     _try {
  1954.         if (argc < 1) {
  1955.             return Result;
  1956.             }
  1957.  
  1958.         while (--argc) {
  1959.             s = *++argv;
  1960.             if (*s == '-' || *s == '/') {
  1961.                 while (*++s) {
  1962.                     switch( tolower( *s ) ) {
  1963.                         case 'm':
  1964.                             MappedFileIO = TRUE;
  1965.                             break;
  1966.  
  1967.                         case 'a':
  1968.                             ASyncIO = TRUE;
  1969.                             break;
  1970.  
  1971.                         case 's':
  1972.                             SyncIO = TRUE;
  1973.                             break;
  1974.  
  1975.                         case 'v':
  1976.                             Verbose = TRUE;
  1977.                             break;
  1978.  
  1979.                         case 'y':
  1980.                             IgnoreCase = TRUE;
  1981.                             break;
  1982.  
  1983.                         case 't':
  1984.                             if (--argc) {
  1985.                                 NumberOfWorkerThreads = atoi( *++argv );
  1986.                                 if (NumberOfWorkerThreads > 0 && NumberOfWorkerThreads < 128) {
  1987.                                     break;
  1988.                                     }
  1989.                                 }
  1990.  
  1991.                             // fall through if -t argument missing.
  1992.  
  1993.                         case '?':
  1994.                         case 'h':
  1995.                         default:
  1996.                             return Result;
  1997.                         }
  1998.                     }
  1999.                 }
  2000.             else
  2001.             if (SearchString == NULL) {
  2002.                 SearchString = s;
  2003.                 }
  2004.             else
  2005.             if (DirectoryPath == NULL) {
  2006.                 DirectoryPath = s;
  2007.                 }
  2008.             else {
  2009.                 return Result;
  2010.                 }
  2011.             }
  2012.  
  2013.         if (SearchString == NULL) {
  2014.             return Result;
  2015.             }
  2016.  
  2017.         SearchStringLength = strlen( SearchString );
  2018.         if (SearchStringLength == 0) {
  2019.             return Result;
  2020.             }
  2021.  
  2022.         if (IgnoreCase) {
  2023.             SearchFunction = _strnicmp;
  2024.             }
  2025.         else {
  2026.             SearchFunction = strncmp;
  2027.             }
  2028.  
  2029.         if (DirectoryPath == NULL) {
  2030.             DirectoryPath = ".";
  2031.             }
  2032.  
  2033.         if (!(MappedFileIO || ASyncIO || SyncIO)) {
  2034.             MappedFileIO = TRUE;
  2035.             }
  2036.  
  2037.         if (Verbose) {
  2038.             fprintf( stderr, "Directory Tree: %s\n", DirectoryPath );
  2039.             fprintf( stderr, "Search String: '%s'\n", SearchString );
  2040.             fprintf( stderr, "Case %ssensitive\n", IgnoreCase ? "in" : "" );
  2041.             fprintf( stderr, "Number of Worker Threads: %u\n", NumberOfWorkerThreads );
  2042.             if (MappedFileIO) {
  2043.                 fprintf( stderr, "Using Mapped File I/O\n" );
  2044.                 }
  2045.             else
  2046.             if (ASyncIO) {
  2047.                 fprintf( stderr, "Using ASynchronous File I/O\n" );
  2048.                 }
  2049.             else
  2050.             if (MappedFileIO) {
  2051.                 fprintf( stderr, "Using Synchronous File I/O\n" );
  2052.                 }
  2053.             }
  2054.  
  2055.         Result = TRUE;
  2056.         return Result;
  2057.         }
  2058.     _finally {
  2059.         if (!Result) {
  2060.             fprintf( stderr, "usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]\n" );
  2061.             fprintf( stderr, "Where...\n" );
  2062.             fprintf( stderr, "    -h - prints this message\n" );
  2063.             fprintf( stderr, "    -v - generates verbose output\n" );
  2064.             fprintf( stderr, "    -y - ignores case when doing comparision\n" );
  2065.             fprintf( stderr, "    -t - specifies the number of threads to use (defaults to 4 * number of processors)\n" );
  2066.             fprintf( stderr, "    -a - uses asynchronous file I/O\n" );
  2067.             fprintf( stderr, "    -s - uses synchronous file I/O\n" );
  2068.             fprintf( stderr, "    -m - uses mapped file I/O (default)\n" );
  2069.             fprintf( stderr, "    SearchString - specifies the text to search for\n" );
  2070.             fprintf( stderr, "    DirectoryPath - specifies the directory to start from (defaults to .)\n" );
  2071.             }
  2072.         }
  2073. }
  2074.