1/*++
2
3Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
4This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution.  The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13
14  MultiThread.c
15
16Abstract:
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
31BUILD_ITEM *
32AddBuildItem (
33  BUILD_ITEM  **BuildList,
34  INT8        *BaseName,
35  INT8        *Processor,
36  INT8        *Makefile
37  )
38/*++
39
40Routine Description:
41
42  Add a build item to a specified build list
43
44Arguments:
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
51Returns:
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
80SOURCE_FILE_ITEM *
81AddSourceFile (
82  BUILD_ITEM  *BuildItem,
83  INT8        *FileName
84  )
85/*++
86
87Routine Description:
88
89  Add a source file for a build item
90
91Arguments:
92
93  BuildItem - build item to add the source file
94  FileName  - source file name to be added
95
96Returns:
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
123DEPENDENCY_ITEM *
124AddDependency (
125  BUILD_ITEM  *BuildList,
126  BUILD_ITEM  *BuildItem,
127  INT8        *BaseName,
128  INT8        AdjustIndex
129  )
130/*++
131
132Routine Description:
133
134  Add a build dependency for a build item in the specified build list
135
136Arguments:
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
143Returns:
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
195void
196FreeBuildList (
197  BUILD_ITEM  *BuildList
198  )
199/*++
200
201Routine Description:
202
203  Free a build list
204
205Arguments:
206
207  BuildList  - build list to be freed
208
209Returns:
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
252COMPONENTS_ITEM *
253AddComponentsItem (
254  COMPONENTS_ITEM  **ComponentsList
255  )
256/*++
257
258Routine Description:
259
260  Add a new components item to a specified components list
261
262Arguments:
263
264  ComponentsList  - components list where the new components item will be added
265
266Returns:
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
300void
301FreeComponentsList (
302  COMPONENTS_ITEM  *ComponentsList
303  )
304/*++
305
306Routine Description:
307
308  Free a components list
309
310Arguments:
311
312  ComponentsList  - components list to be freed
313
314Returns:
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//
333static INT8             mError;            // non-zero means error occurred
334static INT8             mDone;             // non-zero means no more build items available for build
335static UINT32           mThreadNumber;     // thread number
336static INT8             *mBuildDir;        // build directory
337static INT8             mLogDir[MAX_PATH]; // build item log dir
338static CRITICAL_SECTION mCriticalSection;  // critical section object
339static HANDLE           mSemaphoreHandle;  // semaphore for "ready for build" items in mWaitingList
340static HANDLE           mEventHandle;      // event signaled when one build item is finished
341static BUILD_ITEM       *mPendingList;     // build list for build items which are not ready for build
342static BUILD_ITEM       *mWaitingList;     // build list for build items which are ready for build
343static BUILD_ITEM       *mBuildingList;    // build list for build items which are buiding
344static 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//
349static void
350RestoreBuildList (
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//
408static INT8
409CheckSourceFile (
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//
437static INT8
438CheckDependency (
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//
456static INT8
457RunBuildTask (
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//
551static DWORD WINAPI
552ThreadProc (
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
692INT8
693StartMultiThreadBuild (
694  BUILD_ITEM  **BuildList,
695  UINT32      ThreadNumber,
696  INT8        *BuildDir
697  )
698/*++
699
700Routine Description:
701
702  Start multi-thread build for a specified build list
703
704Arguments:
705
706  BuildList     - build list for multi-thread build
707  ThreadNumber  - thread number for multi-thread build
708  BuildDir      - build dir
709
710Returns:
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