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