Home | History | Annotate | Download | only in ProcessDsc
      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 *
     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 *
     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 *
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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