|
|
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.