1 /*++
2 
3 Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   MultiThread.c
15 
16 Abstract:
17 
18   This module is used to add multi-thread build support to ProcessDsc utility
19   to improve the build performance.
20 
21 --*/
22 
23 #include <windows.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <direct.h>
28 #include "Common.h"
29 #include "MultiThread.h"
30 
31 BUILD_ITEM *
AddBuildItem(BUILD_ITEM ** BuildList,INT8 * BaseName,INT8 * Processor,INT8 * Makefile)32 AddBuildItem (
33   BUILD_ITEM  **BuildList,
34   INT8        *BaseName,
35   INT8        *Processor,
36   INT8        *Makefile
37   )
38 /*++
39 
40 Routine Description:
41 
42   Add a build item to a specified build list
43 
44 Arguments:
45 
46   BuildList  - build list where the new build item will be added
47   BaseName   - base name of the new module
48   Processor  - processor type of the new module
49   Makefile   - makefile name of the new module
50 
51 Returns:
52 
53   Pointer to the newly added build item
54 
55 --*/
56 {
57   BUILD_ITEM  *NewBuildItem;
58 
59   //
60   // Create a new build item
61   //
62   NewBuildItem = malloc (sizeof (BUILD_ITEM));
63   if (NewBuildItem == NULL) {
64     return NULL;
65   }
66   memset (NewBuildItem, 0, sizeof (BUILD_ITEM));
67   NewBuildItem->BaseName  = _strdup (BaseName);
68   NewBuildItem->Processor = _strdup (Processor);
69   NewBuildItem->Makefile = _strdup (Makefile);
70 
71   //
72   // Add the build item to the head of the build list
73   //
74   NewBuildItem->Next = *BuildList;
75   *BuildList = NewBuildItem;
76 
77   return NewBuildItem;
78 }
79 
80 SOURCE_FILE_ITEM *
AddSourceFile(BUILD_ITEM * BuildItem,INT8 * FileName)81 AddSourceFile (
82   BUILD_ITEM  *BuildItem,
83   INT8        *FileName
84   )
85 /*++
86 
87 Routine Description:
88 
89   Add a source file for a build item
90 
91 Arguments:
92 
93   BuildItem - build item to add the source file
94   FileName  - source file name to be added
95 
96 Returns:
97 
98   Pointer to the newly added source file item
99 
100 --*/
101 {
102   SOURCE_FILE_ITEM *NewSourceFile;
103 
104   //
105   // Create a new source file item
106   //
107   NewSourceFile = malloc (sizeof (SOURCE_FILE_ITEM));
108   if (NewSourceFile == NULL) {
109     return NULL;
110   }
111   memset (NewSourceFile, 0, sizeof (SOURCE_FILE_ITEM));
112   NewSourceFile->FileName  = _strdup (FileName);
113 
114   //
115   // Add the source file item to the head of the source file list
116   //
117   NewSourceFile->Next = BuildItem->SourceFileList;
118   BuildItem->SourceFileList = NewSourceFile;
119 
120   return NewSourceFile;
121 }
122 
123 DEPENDENCY_ITEM *
AddDependency(BUILD_ITEM * BuildList,BUILD_ITEM * BuildItem,INT8 * BaseName,INT8 AdjustIndex)124 AddDependency (
125   BUILD_ITEM  *BuildList,
126   BUILD_ITEM  *BuildItem,
127   INT8        *BaseName,
128   INT8        AdjustIndex
129   )
130 /*++
131 
132 Routine Description:
133 
134   Add a build dependency for a build item in the specified build list
135 
136 Arguments:
137 
138   BuildList   - build list where to search the dependency
139   BuildItem   - build item to add the dependency
140   BaseName    - dependency module base name
141   AdjustIndex - Adjust BuildItem->Index when non-zero
142 
143 Returns:
144 
145   Pointer to the newly added build dependency
146 
147 --*/
148 {
149   BUILD_ITEM       *TempBuildItem;
150   DEPENDENCY_ITEM  *NewDependency;
151 
152   //
153   // Search the dependency in the build list
154   //
155   TempBuildItem = BuildList;
156   while (TempBuildItem != NULL) {
157     if ((_stricmp (TempBuildItem->BaseName, BaseName) == 0) &&
158         (_stricmp (TempBuildItem->Processor, BuildItem->Processor) == 0) &&
159         (TempBuildItem != BuildItem)) {
160       break;
161     }
162     TempBuildItem = TempBuildItem->Next;
163   }
164   if (TempBuildItem == NULL) {
165     return NULL;
166   }
167 
168   //
169   // This index is used to isolate two modules with same base name and processor.
170   // (ProcessDsc allows duplicate base name libraries.)
171   //
172   if (AdjustIndex) {
173     BuildItem->Index = TempBuildItem->Index + 1;
174   }
175 
176   //
177   // Create a new build dependency item
178   //
179   NewDependency = malloc (sizeof (DEPENDENCY_ITEM));
180   if (NewDependency == NULL) {
181     return NULL;
182   }
183   memset (NewDependency, 0, sizeof (DEPENDENCY_ITEM));
184   NewDependency->Dependency  = TempBuildItem;
185 
186   //
187   // Add the build dependency item to the head of the dependency list
188   //
189   NewDependency->Next = BuildItem->DependencyList;
190   BuildItem->DependencyList = NewDependency;
191 
192   return NewDependency;
193 }
194 
195 void
FreeBuildList(BUILD_ITEM * BuildList)196 FreeBuildList (
197   BUILD_ITEM  *BuildList
198   )
199 /*++
200 
201 Routine Description:
202 
203   Free a build list
204 
205 Arguments:
206 
207   BuildList  - build list to be freed
208 
209 Returns:
210 
211 --*/
212 {
213   BUILD_ITEM       *TempBuildItem;
214   BUILD_ITEM       *FreeBuildItem;
215   SOURCE_FILE_ITEM *TempSourceFile;
216   SOURCE_FILE_ITEM *FreeSourceFile;
217   DEPENDENCY_ITEM  *TempDependency;
218   DEPENDENCY_ITEM  *FreeDependency;
219 
220   TempBuildItem = BuildList;
221   while (TempBuildItem != NULL) {
222     free (TempBuildItem->BaseName);
223     free (TempBuildItem->Processor);
224     free (TempBuildItem->Makefile);
225 
226     //
227     // Free source file list
228     //
229     TempSourceFile = TempBuildItem->SourceFileList;
230     while (TempSourceFile != NULL) {
231       FreeSourceFile = TempSourceFile;
232       TempSourceFile = TempSourceFile->Next;
233       free (FreeSourceFile);
234     }
235 
236     //
237     // Free dependency list
238     //
239     TempDependency = TempBuildItem->DependencyList;
240     while (TempDependency != NULL) {
241       FreeDependency = TempDependency;
242       TempDependency = TempDependency->Next;
243       free (FreeDependency);
244     }
245 
246     FreeBuildItem = TempBuildItem;
247     TempBuildItem = TempBuildItem->Next;
248     free (FreeBuildItem);
249   }
250 }
251 
252 COMPONENTS_ITEM *
AddComponentsItem(COMPONENTS_ITEM ** ComponentsList)253 AddComponentsItem (
254   COMPONENTS_ITEM  **ComponentsList
255   )
256 /*++
257 
258 Routine Description:
259 
260   Add a new components item to a specified components list
261 
262 Arguments:
263 
264   ComponentsList  - components list where the new components item will be added
265 
266 Returns:
267 
268   Pointer to the newly added components item
269 
270 --*/
271 {
272   COMPONENTS_ITEM  *NewComponents;
273   COMPONENTS_ITEM  *TempComponents;
274 
275   //
276   // Create a new components item
277   //
278   NewComponents = malloc (sizeof (COMPONENTS_ITEM));
279   if (NewComponents == NULL) {
280     return NULL;
281   }
282   memset (NewComponents, 0, sizeof (COMPONENTS_ITEM));
283 
284   //
285   // Add the components item to the tail of the components list
286   //
287   TempComponents = *ComponentsList;
288   if (TempComponents == NULL) {
289     *ComponentsList = NewComponents;
290   } else {
291     while (TempComponents->Next != NULL) {
292       TempComponents = TempComponents->Next;
293     }
294     TempComponents->Next = NewComponents;
295   }
296 
297   return NewComponents;
298 }
299 
300 void
FreeComponentsList(COMPONENTS_ITEM * ComponentsList)301 FreeComponentsList (
302   COMPONENTS_ITEM  *ComponentsList
303   )
304 /*++
305 
306 Routine Description:
307 
308   Free a components list
309 
310 Arguments:
311 
312   ComponentsList  - components list to be freed
313 
314 Returns:
315 
316 --*/
317 {
318   COMPONENTS_ITEM  *TempComponents;
319   COMPONENTS_ITEM  *FreeComponents;
320 
321   TempComponents = ComponentsList;
322   while (TempComponents != NULL) {
323     FreeBuildList (TempComponents->BuildList);
324     FreeComponents = TempComponents;
325     TempComponents = TempComponents->Next;
326     free (FreeComponents);
327   }
328 }
329 
330 //
331 // Module globals for multi-thread build
332 //
333 static INT8             mError;            // non-zero means error occurred
334 static INT8             mDone;             // non-zero means no more build items available for build
335 static UINT32           mThreadNumber;     // thread number
336 static INT8             *mBuildDir;        // build directory
337 static INT8             mLogDir[MAX_PATH]; // build item log dir
338 static CRITICAL_SECTION mCriticalSection;  // critical section object
339 static HANDLE           mSemaphoreHandle;  // semaphore for "ready for build" items in mWaitingList
340 static HANDLE           mEventHandle;      // event signaled when one build item is finished
341 static BUILD_ITEM       *mPendingList;     // build list for build items which are not ready for build
342 static BUILD_ITEM       *mWaitingList;     // build list for build items which are ready for build
343 static BUILD_ITEM       *mBuildingList;    // build list for build items which are buiding
344 static BUILD_ITEM       *mDoneList;        // build list for build items which already finish the build
345 
346 //
347 // Restore the BuildList (not care about the sequence of the build items)
348 //
349 static void
RestoreBuildList(BUILD_ITEM ** BuildList)350 RestoreBuildList (
351   BUILD_ITEM  **BuildList
352   )
353 {
354   BUILD_ITEM  *TempBuildItem;
355 
356   if (mPendingList != NULL) {
357     //
358     // Add the mPendingList to the header of *BuildList
359     //
360     TempBuildItem = mPendingList;
361     while (TempBuildItem->Next != NULL) {
362       TempBuildItem = TempBuildItem->Next;
363     }
364     TempBuildItem->Next = *BuildList;
365     *BuildList = mPendingList;
366   }
367 
368   if (mWaitingList != NULL) {
369     //
370     // Add the mWaitingList to the header of *BuildList
371     //
372     TempBuildItem = mWaitingList;
373     while (TempBuildItem->Next != NULL) {
374       TempBuildItem = TempBuildItem->Next;
375     }
376     TempBuildItem->Next = *BuildList;
377     *BuildList = mWaitingList;
378   }
379 
380   if (mBuildingList != NULL) {
381     //
382     // Add the mBuildingList to the header of *BuildList
383     //
384     TempBuildItem = mBuildingList;
385     while (TempBuildItem->Next != NULL) {
386       TempBuildItem = TempBuildItem->Next;
387     }
388     TempBuildItem->Next = *BuildList;
389     *BuildList = mBuildingList;
390   }
391 
392   if (mDoneList != NULL) {
393     //
394     // Add the mDoneList to the header of *BuildList
395     //
396     TempBuildItem = mDoneList;
397     while (TempBuildItem->Next != NULL) {
398       TempBuildItem = TempBuildItem->Next;
399     }
400     TempBuildItem->Next = *BuildList;
401     *BuildList = mDoneList;
402   }
403 }
404 
405 //
406 // Return non-zero when no source file build conflict
407 //
408 static INT8
CheckSourceFile(SOURCE_FILE_ITEM * SourceFileList)409 CheckSourceFile (
410   SOURCE_FILE_ITEM  *SourceFileList
411   )
412 {
413   BUILD_ITEM        *TempBuildItem;
414   SOURCE_FILE_ITEM  *TempSourceFile;
415 
416   while (SourceFileList != NULL) {
417     TempBuildItem = mBuildingList;
418     while (TempBuildItem != NULL) {
419       TempSourceFile = TempBuildItem->SourceFileList;
420       while (TempSourceFile != NULL) {
421         if (_stricmp (SourceFileList->FileName, TempSourceFile->FileName) == 0) {
422           return 0;
423         }
424         TempSourceFile = TempSourceFile->Next;
425       }
426       TempBuildItem = TempBuildItem->Next;
427     }
428     SourceFileList = SourceFileList->Next;
429   }
430 
431   return 1;
432 }
433 
434 //
435 // Return non-zero when all the dependency build items has been built
436 //
437 static INT8
CheckDependency(DEPENDENCY_ITEM * DependencyList)438 CheckDependency (
439   DEPENDENCY_ITEM  *DependencyList
440   )
441 {
442   while (DependencyList != NULL) {
443     if (!(DependencyList->Dependency->CompleteFlag)) {
444       return 0;
445     }
446     DependencyList = DependencyList->Next;
447   }
448 
449   return 1;
450 }
451 
452 //
453 // Run the build task. The system() function call  will cause stdout conflict
454 // in multi-thread envroment, so implement this through CreateProcess().
455 //
456 static INT8
RunBuildTask(INT8 * WorkingDir,INT8 * LogFile,INT8 * BuildCmd)457 RunBuildTask (
458   INT8  *WorkingDir,
459   INT8  *LogFile,
460   INT8  *BuildCmd
461   )
462 {
463   HANDLE                FileHandle;
464   SECURITY_ATTRIBUTES   SecAttr;
465   PROCESS_INFORMATION   ProcInfo;
466   STARTUPINFO           StartInfo;
467   BOOL                  FuncRetn;
468   DWORD                 ExitCode;
469 
470   //
471   // Init SecAttr
472   //
473   SecAttr.nLength              = sizeof (SECURITY_ATTRIBUTES);
474   SecAttr.bInheritHandle       = TRUE;
475   SecAttr.lpSecurityDescriptor = NULL;
476 
477   //
478   // Create the log file
479   //
480   FileHandle = CreateFile (
481                  LogFile,                // file to create
482                  GENERIC_WRITE,          // open for writing
483                  0,                      // do not share
484                  &SecAttr,               // can be inherited by child processes
485                  CREATE_ALWAYS,          // overwrite existing
486                  FILE_ATTRIBUTE_NORMAL,  // normal file
487                  NULL                    // no attr. template
488                  );
489 
490   if (FileHandle == INVALID_HANDLE_VALUE) {
491       EnterCriticalSection (&mCriticalSection);
492       Error (NULL, 0, 0, NULL, "could not open file %s", LogFile);
493       LeaveCriticalSection (&mCriticalSection);
494       return 1;
495   }
496 
497   //
498   // Init ProcInfo and StartInfo
499   //
500   ZeroMemory (&ProcInfo, sizeof (PROCESS_INFORMATION));
501   ZeroMemory (&StartInfo, sizeof (STARTUPINFO));
502   StartInfo.cb         = sizeof (STARTUPINFO);
503   StartInfo.hStdError  = FileHandle;
504   StartInfo.hStdOutput = FileHandle;
505   StartInfo.hStdInput  = GetStdHandle (STD_INPUT_HANDLE);
506   StartInfo.dwFlags    = STARTF_USESTDHANDLES;
507 
508   //
509   // Create the child process
510   //
511   FuncRetn = CreateProcess (
512                NULL,          // no application name
513                BuildCmd,      // command line
514                NULL,          // process security attributes
515                NULL,          // primary thread security attributes
516                TRUE,          // handles are inherited
517                0,             // creation flags
518                NULL,          // use parent's environment
519                WorkingDir,    // set current directory
520                &StartInfo,    // STARTUPINFO pointer
521                &ProcInfo      // receives PROCESS_INFORMATION
522                );
523 
524   if (FuncRetn == FALSE) {
525     EnterCriticalSection (&mCriticalSection);
526     Error (NULL, 0, 0, NULL, "could not create child process");
527     LeaveCriticalSection (&mCriticalSection);
528     CloseHandle (FileHandle);
529     return 1;
530   }
531 
532   //
533   // Wait until child process exits
534   //
535   WaitForSingleObject (ProcInfo.hProcess, INFINITE);
536   GetExitCodeProcess (ProcInfo.hProcess, &ExitCode);
537   CloseHandle (ProcInfo.hProcess);
538   CloseHandle (ProcInfo.hThread);
539   CloseHandle (FileHandle);
540 
541   if (ExitCode != 0) {
542     return 1;
543   } else {
544     return 0;
545   }
546 }
547 
548 //
549 // Thread function
550 //
551 static DWORD WINAPI
ThreadProc(LPVOID lpParam)552 ThreadProc (
553   LPVOID lpParam
554   )
555 {
556   UINT32      ThreadId;
557   BUILD_ITEM  *PreviousBuildItem;
558   BUILD_ITEM  *CurrentBuildItem;
559   BUILD_ITEM  *NextBuildItem;
560   INT8        WorkingDir[MAX_PATH];
561   INT8        LogFile[MAX_PATH];
562   INT8        BuildCmd[MAX_PATH];
563 
564   ThreadId = (UINT32)lpParam;
565   //
566   // Loop until error occurred or no more build items available for build
567   //
568   for (;;) {
569     WaitForSingleObject (mSemaphoreHandle, INFINITE);
570     if (mError || mDone) {
571       return 0;
572     }
573 
574     //
575     // When code runs here, there must have one build item available for this
576     // thread. Loop until error occurred or get one build item for build.
577     //
578     for (;;) {
579       EnterCriticalSection (&mCriticalSection);
580       PreviousBuildItem = NULL;
581       CurrentBuildItem  = mWaitingList;
582       while (CurrentBuildItem != NULL) {
583         NextBuildItem = CurrentBuildItem->Next;
584         //
585         // CheckSourceFile() is to avoid concurrently build the same source file
586         // which may cause the muti-thread build failure
587         //
588         if (CheckSourceFile (CurrentBuildItem->SourceFileList)) {
589           //
590           // Move the current build item from mWaitingList
591           //
592           if (PreviousBuildItem != NULL) {
593             PreviousBuildItem->Next = NextBuildItem;
594           } else {
595             mWaitingList = NextBuildItem;
596           }
597           //
598           // Add the current build item to the head of mBuildingList
599           //
600           CurrentBuildItem->Next = mBuildingList;
601           mBuildingList = CurrentBuildItem;
602           //
603           // If no more build items is pending or waiting for build,
604           // wake up every child thread for exit.
605           //
606           if ((mPendingList == NULL) && (mWaitingList == NULL)) {
607             mDone = 1;
608             //
609             // Make sure to wake up every child thread for exit
610             //
611             ReleaseSemaphore (mSemaphoreHandle, mThreadNumber, NULL);
612           }
613           break;
614         }
615         PreviousBuildItem = CurrentBuildItem;
616         CurrentBuildItem  = NextBuildItem;
617       }
618       if (CurrentBuildItem != NULL) {
619         //
620         // Display build item info
621         //
622         printf ("\t[Thread_%d] nmake -nologo -f %s all\n", ThreadId, CurrentBuildItem->Makefile);
623         //
624         // Prepare build task
625         //
626         sprintf (WorkingDir, "%s\\%s", mBuildDir, CurrentBuildItem->Processor);
627         sprintf (LogFile, "%s\\%s_%s_%d.txt", mLogDir, CurrentBuildItem->BaseName,
628                  CurrentBuildItem->Processor, CurrentBuildItem->Index);
629         sprintf (BuildCmd, "nmake -nologo -f %s all", CurrentBuildItem->Makefile);
630         LeaveCriticalSection (&mCriticalSection);
631         break;
632       } else {
633         LeaveCriticalSection (&mCriticalSection);
634         //
635         // All the build items in mWaitingList have source file conflict with
636         // mBuildingList. This rarely hapeens. Need wait for the build items in
637         // mBuildingList to be finished by other child threads.
638         //
639         Sleep (1000);
640         if (mError) {
641           return 0;
642         }
643       }
644     }
645 
646     //
647     // Start to build the CurrentBuildItem
648     //
649     if (RunBuildTask (WorkingDir, LogFile, BuildCmd)) {
650       //
651       // Build failure
652       //
653       mError = 1;
654       //
655       // Make sure to wake up every child thread for exit
656       //
657       ReleaseSemaphore (mSemaphoreHandle, mThreadNumber, NULL);
658       SetEvent(mEventHandle);
659 
660       return mError;
661     } else {
662       //
663       // Build success
664       //
665       CurrentBuildItem->CompleteFlag = 1;
666 
667       EnterCriticalSection (&mCriticalSection);
668       //
669       // Move this build item from mBuildingList
670       //
671       if (mBuildingList == CurrentBuildItem) {
672         mBuildingList = mBuildingList->Next;
673       } else {
674         NextBuildItem = mBuildingList;
675         while (NextBuildItem->Next != CurrentBuildItem) {
676           NextBuildItem = NextBuildItem->Next;
677         }
678         NextBuildItem->Next = CurrentBuildItem->Next;
679       }
680       //
681       // Add this build item to mDoneList
682       //
683       CurrentBuildItem->Next = mDoneList;
684       mDoneList = CurrentBuildItem;
685       LeaveCriticalSection (&mCriticalSection);
686 
687       SetEvent(mEventHandle);
688     }
689   }
690 }
691 
692 INT8
StartMultiThreadBuild(BUILD_ITEM ** BuildList,UINT32 ThreadNumber,INT8 * BuildDir)693 StartMultiThreadBuild (
694   BUILD_ITEM  **BuildList,
695   UINT32      ThreadNumber,
696   INT8        *BuildDir
697   )
698 /*++
699 
700 Routine Description:
701 
702   Start multi-thread build for a specified build list
703 
704 Arguments:
705 
706   BuildList     - build list for multi-thread build
707   ThreadNumber  - thread number for multi-thread build
708   BuildDir      - build dir
709 
710 Returns:
711 
712   0             - Successfully finished the multi-thread build
713   other value   - Build failure
714 
715 --*/
716 {
717   UINT32        Index;
718   UINT32        Count;
719   BUILD_ITEM    *PreviousBuildItem;
720   BUILD_ITEM    *CurrentBuildItem;
721   BUILD_ITEM    *NextBuildItem;
722   HANDLE        *ThreadHandle;
723   INT8          Cmd[MAX_PATH];
724 
725   mError        = 0;
726   mDone         = 0;
727   mThreadNumber = ThreadNumber;
728   mBuildDir     = BuildDir;
729   mPendingList  = *BuildList;
730   *BuildList    = NULL;
731   mWaitingList  = NULL;
732   mBuildingList = NULL;
733   mDoneList     = NULL;
734 
735   //
736   // Do nothing when mPendingList is empty
737   //
738   if (mPendingList == NULL) {
739     return 0;
740   }
741 
742   //
743   // Get build item count of mPendingList
744   //
745   Count = 0;
746   CurrentBuildItem = mPendingList;
747   while (CurrentBuildItem != NULL) {
748     Count++;
749     CurrentBuildItem = CurrentBuildItem->Next;
750   }
751 
752   //
753   // The semaphore is also used to wake up child threads for exit,
754   // so need to make sure "maximum count" >= "thread number".
755   //
756   if (Count < ThreadNumber) {
757     Count = ThreadNumber;
758   }
759 
760   //
761   // Init mSemaphoreHandle
762   //
763   mSemaphoreHandle = CreateSemaphore (
764                        NULL,       // default security attributes
765                        0,          // initial count
766                        Count,      // maximum count
767                        NULL        // unnamed semaphore
768                        );
769   if (mSemaphoreHandle == NULL) {
770     Error (NULL, 0, 0, NULL, "failed to create semaphore");
771     RestoreBuildList (BuildList);
772     return 1;
773   }
774 
775   //
776   // Init mEventHandle
777   //
778   mEventHandle = CreateEvent(
779                    NULL,     // default security attributes
780                    FALSE,    // auto-reset event
781                    TRUE,     // initial state is signaled
782                    NULL      // object not named
783                    );
784   if (mEventHandle == NULL) {
785     Error (NULL, 0, 0, NULL, "failed to create event");
786     CloseHandle (mSemaphoreHandle);
787     RestoreBuildList (BuildList);
788     return 1;
789   }
790 
791   //
792   // Init mCriticalSection
793   //
794   InitializeCriticalSection (&mCriticalSection);
795 
796   //
797   // Create build item log dir
798   //
799   sprintf (mLogDir, "%s\\Log", mBuildDir);
800   _mkdir (mLogDir);
801 
802   //
803   // Create child threads for muti-thread build
804   //
805   ThreadHandle = malloc (ThreadNumber * sizeof (HANDLE));
806   if (ThreadHandle == NULL) {
807     Error (NULL, 0, 0, NULL, "failed to allocate memory");
808     CloseHandle (mSemaphoreHandle);
809     CloseHandle (mEventHandle);
810     RestoreBuildList (BuildList);
811     return 1;
812   }
813   for (Index = 0; Index < ThreadNumber; Index++) {
814     ThreadHandle[Index] = CreateThread (
815                             NULL,           // default security attributes
816                             0,              // use default stack size
817                             ThreadProc,     // thread function
818                             (LPVOID)Index,  // argument to thread function: use Index as thread id
819                             0,              // use default creation flags
820                             NULL            // thread identifier not needed
821                             );
822     if (ThreadHandle[Index] == NULL) {
823       Error (NULL, 0, 0, NULL, "failed to create Thread_%d", Index);
824       mError       = 1;
825       ThreadNumber = Index;
826       //
827       // Make sure to wake up every child thread for exit
828       //
829       ReleaseSemaphore (mSemaphoreHandle, ThreadNumber, NULL);
830       break;
831     }
832   }
833 
834   //
835   // Loop until error occurred or no more build items pending for build
836   //
837   for (;;) {
838     WaitForSingleObject (mEventHandle, INFINITE);
839     if (mError) {
840       break;
841     }
842     Count = 0;
843 
844     EnterCriticalSection (&mCriticalSection);
845     PreviousBuildItem = NULL;
846     CurrentBuildItem  = mPendingList;
847     while (CurrentBuildItem != NULL) {
848       NextBuildItem = CurrentBuildItem->Next;
849       if (CheckDependency (CurrentBuildItem->DependencyList)) {
850         //
851         // Move the current build item from mPendingList
852         //
853         if (PreviousBuildItem != NULL) {
854           PreviousBuildItem->Next = NextBuildItem;
855         } else {
856           mPendingList = NextBuildItem;
857         }
858         //
859         // Add the current build item to the head of mWaitingList
860         //
861         CurrentBuildItem->Next = mWaitingList;
862         mWaitingList = CurrentBuildItem;
863         Count++;
864       } else {
865         PreviousBuildItem = CurrentBuildItem;
866       }
867       CurrentBuildItem  = NextBuildItem;
868     }
869     LeaveCriticalSection (&mCriticalSection);
870 
871     ReleaseSemaphore (mSemaphoreHandle, Count, NULL);
872     if (mPendingList == NULL) {
873       break;
874     }
875   }
876 
877   //
878   // Wait until all threads have terminated
879   //
880   WaitForMultipleObjects (ThreadNumber, ThreadHandle, TRUE, INFINITE);
881 
882   if (mError && (mBuildingList != NULL)) {
883     //
884     // Dump build failure log of the first build item which doesn't finish the build
885     //
886     printf ("\tnmake -nologo -f %s all\n", mBuildingList->Makefile);
887     sprintf (Cmd, "type %s\\%s_%s_%d.txt 2>NUL", mLogDir, mBuildingList->BaseName,
888              mBuildingList->Processor, mBuildingList->Index);
889     _flushall ();
890     if (system (Cmd)) {
891       Error (NULL, 0, 0, NULL, "failed to run \"%s\"", Cmd);
892     }
893   }
894 
895   DeleteCriticalSection (&mCriticalSection);
896   for (Index = 0; Index < ThreadNumber; Index++) {
897     CloseHandle (ThreadHandle[Index]);
898   }
899   free (ThreadHandle);
900   CloseHandle (mSemaphoreHandle);
901   CloseHandle (mEventHandle);
902   RestoreBuildList (BuildList);
903 
904   return mError;
905 }
906