]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EdkCompatibilityPkg/Sample/Tools/Source/ProcessDsc/MultiThread.c
Sync all bug fixes between EDK1.04 and EDK1.06 into EdkCompatibilityPkg.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / ProcessDsc / MultiThread.c
diff --git a/EdkCompatibilityPkg/Sample/Tools/Source/ProcessDsc/MultiThread.c b/EdkCompatibilityPkg/Sample/Tools/Source/ProcessDsc/MultiThread.c
new file mode 100644 (file)
index 0000000..b6a72ec
--- /dev/null
@@ -0,0 +1,905 @@
+/*++\r
+\r
+Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+\r
+Module Name:\r
+\r
+  MultiThread.c\r
+\r
+Abstract:\r
+\r
+  This module is used to add multi-thread build support to ProcessDsc utility \r
+  to improve the build performance. \r
+\r
+--*/\r
+\r
+#include <windows.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <direct.h>\r
+#include "Common.h"\r
+#include "MultiThread.h"\r
+\r
+BUILD_ITEM *\r
+AddBuildItem (\r
+  BUILD_ITEM  **BuildList,\r
+  INT8        *BaseName,\r
+  INT8        *Processor,\r
+  INT8        *Makefile\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Add a build item to a specified build list\r
+\r
+Arguments:\r
+  \r
+  BuildList  - build list where the new build item will be added\r
+  BaseName   - base name of the new module\r
+  Processor  - processor type of the new module\r
+  Makefile   - makefile name of the new module\r
+\r
+Returns:\r
+\r
+  Pointer to the newly added build item\r
+\r
+--*/\r
+{\r
+  BUILD_ITEM  *NewBuildItem;\r
+  \r
+  //\r
+  // Create a new build item\r
+  //\r
+  NewBuildItem = malloc (sizeof (BUILD_ITEM));\r
+  if (NewBuildItem == NULL) {\r
+    return NULL;\r
+  }\r
+  memset (NewBuildItem, 0, sizeof (BUILD_ITEM));\r
+  NewBuildItem->BaseName  = _strdup (BaseName);\r
+  NewBuildItem->Processor = _strdup (Processor);\r
+  NewBuildItem->Makefile = _strdup (Makefile);\r
+  \r
+  //\r
+  // Add the build item to the head of the build list\r
+  //\r
+  NewBuildItem->Next = *BuildList;\r
+  *BuildList = NewBuildItem;\r
+  \r
+  return NewBuildItem;\r
+}\r
+\r
+SOURCE_FILE_ITEM *\r
+AddSourceFile (\r
+  BUILD_ITEM  *BuildItem, \r
+  INT8        *FileName\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Add a source file for a build item\r
+\r
+Arguments:\r
+  \r
+  BuildItem - build item to add the source file\r
+  FileName  - source file name to be added\r
+\r
+Returns:\r
+\r
+  Pointer to the newly added source file item\r
+\r
+--*/\r
+{\r
+  SOURCE_FILE_ITEM *NewSourceFile;\r
+  \r
+  //\r
+  // Create a new source file item\r
+  //\r
+  NewSourceFile = malloc (sizeof (SOURCE_FILE_ITEM));\r
+  if (NewSourceFile == NULL) {\r
+    return NULL;\r
+  }\r
+  memset (NewSourceFile, 0, sizeof (SOURCE_FILE_ITEM));\r
+  NewSourceFile->FileName  = _strdup (FileName);\r
+  \r
+  //\r
+  // Add the source file item to the head of the source file list\r
+  //\r
+  NewSourceFile->Next = BuildItem->SourceFileList;\r
+  BuildItem->SourceFileList = NewSourceFile;\r
+  \r
+  return NewSourceFile; \r
+}\r
+\r
+DEPENDENCY_ITEM *\r
+AddDependency (\r
+  BUILD_ITEM  *BuildList, \r
+  BUILD_ITEM  *BuildItem, \r
+  INT8        *BaseName,\r
+  INT8        AdjustIndex\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Add a build dependency for a build item in the specified build list\r
+\r
+Arguments:\r
+  \r
+  BuildList   - build list where to search the dependency\r
+  BuildItem   - build item to add the dependency\r
+  BaseName    - dependency module base name\r
+  AdjustIndex - Adjust BuildItem->Index when non-zero\r
+\r
+Returns:\r
+\r
+  Pointer to the newly added build dependency\r
+\r
+--*/\r
+{\r
+  BUILD_ITEM       *TempBuildItem;\r
+  DEPENDENCY_ITEM  *NewDependency;\r
+  \r
+  //\r
+  // Search the dependency in the build list\r
+  //\r
+  TempBuildItem = BuildList;\r
+  while (TempBuildItem != NULL) {\r
+    if ((_stricmp (TempBuildItem->BaseName, BaseName) == 0) &&\r
+        (_stricmp (TempBuildItem->Processor, BuildItem->Processor) == 0) &&\r
+        (TempBuildItem != BuildItem)) {\r
+      break;\r
+    }\r
+    TempBuildItem = TempBuildItem->Next;\r
+  }\r
+  if (TempBuildItem == NULL) {\r
+    return NULL;\r
+  }\r
+  \r
+  //\r
+  // This index is used to isolate two modules with same base name and processor.\r
+  // (ProcessDsc allows duplicate base name libraries.)\r
+  //\r
+  if (AdjustIndex) {\r
+    BuildItem->Index = TempBuildItem->Index + 1;\r
+  }\r
+  \r
+  //\r
+  // Create a new build dependency item\r
+  //\r
+  NewDependency = malloc (sizeof (DEPENDENCY_ITEM));\r
+  if (NewDependency == NULL) {\r
+    return NULL;\r
+  }\r
+  memset (NewDependency, 0, sizeof (DEPENDENCY_ITEM));\r
+  NewDependency->Dependency  = TempBuildItem;\r
+  \r
+  //\r
+  // Add the build dependency item to the head of the dependency list\r
+  //\r
+  NewDependency->Next = BuildItem->DependencyList;\r
+  BuildItem->DependencyList = NewDependency;\r
+  \r
+  return NewDependency; \r
+}\r
+\r
+void\r
+FreeBuildList (\r
+  BUILD_ITEM  *BuildList\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Free a build list\r
+\r
+Arguments:\r
+  \r
+  BuildList  - build list to be freed\r
+\r
+Returns:\r
+\r
+--*/\r
+{\r
+  BUILD_ITEM       *TempBuildItem;\r
+  BUILD_ITEM       *FreeBuildItem;\r
+  SOURCE_FILE_ITEM *TempSourceFile;\r
+  SOURCE_FILE_ITEM *FreeSourceFile;\r
+  DEPENDENCY_ITEM  *TempDependency;\r
+  DEPENDENCY_ITEM  *FreeDependency;\r
+  \r
+  TempBuildItem = BuildList;\r
+  while (TempBuildItem != NULL) {\r
+    free (TempBuildItem->BaseName);\r
+    free (TempBuildItem->Processor);\r
+    free (TempBuildItem->Makefile);\r
+\r
+    //\r
+    // Free source file list\r
+    //\r
+    TempSourceFile = TempBuildItem->SourceFileList;\r
+    while (TempSourceFile != NULL) {\r
+      FreeSourceFile = TempSourceFile;\r
+      TempSourceFile = TempSourceFile->Next;\r
+      free (FreeSourceFile);\r
+    }\r
+\r
+    //\r
+    // Free dependency list\r
+    //\r
+    TempDependency = TempBuildItem->DependencyList;\r
+    while (TempDependency != NULL) {\r
+      FreeDependency = TempDependency;\r
+      TempDependency = TempDependency->Next;\r
+      free (FreeDependency);\r
+    }\r
+\r
+    FreeBuildItem = TempBuildItem;\r
+    TempBuildItem = TempBuildItem->Next;\r
+    free (FreeBuildItem);\r
+  }\r
+}\r
+\r
+COMPONENTS_ITEM *\r
+AddComponentsItem (\r
+  COMPONENTS_ITEM  **ComponentsList\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Add a new components item to a specified components list\r
+\r
+Arguments:\r
+  \r
+  ComponentsList  - components list where the new components item will be added\r
+\r
+Returns:\r
+\r
+  Pointer to the newly added components item\r
+\r
+--*/\r
+{\r
+  COMPONENTS_ITEM  *NewComponents;\r
+  COMPONENTS_ITEM  *TempComponents;\r
+  \r
+  //\r
+  // Create a new components item\r
+  //\r
+  NewComponents = malloc (sizeof (COMPONENTS_ITEM));\r
+  if (NewComponents == NULL) {\r
+    return NULL;\r
+  }\r
+  memset (NewComponents, 0, sizeof (COMPONENTS_ITEM));\r
+  \r
+  //\r
+  // Add the components item to the tail of the components list\r
+  //\r
+  TempComponents = *ComponentsList;\r
+  if (TempComponents == NULL) {\r
+    *ComponentsList = NewComponents;\r
+  } else {\r
+    while (TempComponents->Next != NULL) {\r
+      TempComponents = TempComponents->Next;\r
+    }\r
+    TempComponents->Next = NewComponents;\r
+  }\r
+  \r
+  return NewComponents;\r
+}\r
+\r
+void\r
+FreeComponentsList (\r
+  COMPONENTS_ITEM  *ComponentsList\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Free a components list\r
+\r
+Arguments:\r
+  \r
+  ComponentsList  - components list to be freed\r
+\r
+Returns:\r
+\r
+--*/\r
+{\r
+  COMPONENTS_ITEM  *TempComponents;\r
+  COMPONENTS_ITEM  *FreeComponents;\r
+  \r
+  TempComponents = ComponentsList;\r
+  while (TempComponents != NULL) {\r
+    FreeBuildList (TempComponents->BuildList);\r
+    FreeComponents = TempComponents;\r
+    TempComponents = TempComponents->Next;\r
+    free (FreeComponents);\r
+  }\r
+}\r
+\r
+//\r
+// Module globals for multi-thread build\r
+//\r
+static INT8             mError;            // non-zero means error occurred\r
+static INT8             mDone;             // non-zero means no more build items available for build\r
+static UINT32           mThreadNumber;     // thread number\r
+static INT8             *mBuildDir;        // build directory\r
+static INT8             mLogDir[MAX_PATH]; // build item log dir\r
+static CRITICAL_SECTION mCriticalSection;  // critical section object\r
+static HANDLE           mSemaphoreHandle;  // semaphore for "ready for build" items in mWaitingList\r
+static HANDLE           mEventHandle;      // event signaled when one build item is finished\r
+static BUILD_ITEM       *mPendingList;     // build list for build items which are not ready for build\r
+static BUILD_ITEM       *mWaitingList;     // build list for build items which are ready for build\r
+static BUILD_ITEM       *mBuildingList;    // build list for build items which are buiding\r
+static BUILD_ITEM       *mDoneList;        // build list for build items which already finish the build\r
+\r
+//\r
+// Restore the BuildList (not care about the sequence of the build items)\r
+//\r
+static void\r
+RestoreBuildList (\r
+  BUILD_ITEM  **BuildList\r
+  )\r
+{\r
+  BUILD_ITEM  *TempBuildItem;\r
+  \r
+  if (mPendingList != NULL) {\r
+    //\r
+    // Add the mPendingList to the header of *BuildList\r
+    //\r
+    TempBuildItem = mPendingList;\r
+    while (TempBuildItem->Next != NULL) {\r
+      TempBuildItem = TempBuildItem->Next;\r
+    }\r
+    TempBuildItem->Next = *BuildList;\r
+    *BuildList = mPendingList;\r
+  }\r
+\r
+  if (mWaitingList != NULL) {\r
+    //\r
+    // Add the mWaitingList to the header of *BuildList\r
+    //\r
+    TempBuildItem = mWaitingList;\r
+    while (TempBuildItem->Next != NULL) {\r
+      TempBuildItem = TempBuildItem->Next;\r
+    }\r
+    TempBuildItem->Next = *BuildList;\r
+    *BuildList = mWaitingList;\r
+  }\r
+\r
+  if (mBuildingList != NULL) {\r
+    //\r
+    // Add the mBuildingList to the header of *BuildList\r
+    //\r
+    TempBuildItem = mBuildingList;\r
+    while (TempBuildItem->Next != NULL) {\r
+      TempBuildItem = TempBuildItem->Next;\r
+    }\r
+    TempBuildItem->Next = *BuildList;\r
+    *BuildList = mBuildingList;\r
+  }\r
+\r
+  if (mDoneList != NULL) {\r
+    //\r
+    // Add the mDoneList to the header of *BuildList\r
+    //\r
+    TempBuildItem = mDoneList;\r
+    while (TempBuildItem->Next != NULL) {\r
+      TempBuildItem = TempBuildItem->Next;\r
+    }\r
+    TempBuildItem->Next = *BuildList;\r
+    *BuildList = mDoneList;\r
+  }\r
+}\r
+\r
+//\r
+// Return non-zero when no source file build conflict\r
+//\r
+static INT8\r
+CheckSourceFile (\r
+  SOURCE_FILE_ITEM  *SourceFileList\r
+  )\r
+{\r
+  BUILD_ITEM        *TempBuildItem;\r
+  SOURCE_FILE_ITEM  *TempSourceFile;\r
+  \r
+  while (SourceFileList != NULL) {\r
+    TempBuildItem = mBuildingList;\r
+    while (TempBuildItem != NULL) {\r
+      TempSourceFile = TempBuildItem->SourceFileList;\r
+      while (TempSourceFile != NULL) {\r
+        if (_stricmp (SourceFileList->FileName, TempSourceFile->FileName) == 0) {\r
+          return 0;\r
+        }\r
+        TempSourceFile = TempSourceFile->Next;\r
+      }\r
+      TempBuildItem = TempBuildItem->Next;\r
+    }\r
+    SourceFileList = SourceFileList->Next;\r
+  }\r
+  \r
+  return 1;\r
+}\r
+\r
+//\r
+// Return non-zero when all the dependency build items has been built\r
+//\r
+static INT8\r
+CheckDependency (\r
+  DEPENDENCY_ITEM  *DependencyList\r
+  )\r
+{\r
+  while (DependencyList != NULL) {\r
+    if (!(DependencyList->Dependency->CompleteFlag)) {\r
+      return 0;\r
+    }\r
+    DependencyList = DependencyList->Next;\r
+  }\r
+  \r
+  return 1;\r
+}\r
+\r
+//\r
+// Run the build task. The system() function call  will cause stdout conflict \r
+// in multi-thread envroment, so implement this through CreateProcess().\r
+//\r
+static INT8\r
+RunBuildTask (\r
+  INT8  *WorkingDir, \r
+  INT8  *LogFile, \r
+  INT8  *BuildCmd\r
+  )\r
+{\r
+  HANDLE                FileHandle;\r
+  SECURITY_ATTRIBUTES   SecAttr;\r
+  PROCESS_INFORMATION   ProcInfo; \r
+  STARTUPINFO           StartInfo;\r
+  BOOL                  FuncRetn;\r
+  DWORD                 ExitCode;\r
+  \r
+  //\r
+  // Init SecAttr\r
+  //\r
+  SecAttr.nLength              = sizeof (SECURITY_ATTRIBUTES); \r
+  SecAttr.bInheritHandle       = TRUE; \r
+  SecAttr.lpSecurityDescriptor = NULL;\r
+  \r
+  //\r
+  // Create the log file\r
+  //\r
+  FileHandle = CreateFile (\r
+                 LogFile,                // file to create\r
+                 GENERIC_WRITE,          // open for writing\r
+                 0,                      // do not share\r
+                 &SecAttr,               // can be inherited by child processes\r
+                 CREATE_ALWAYS,          // overwrite existing\r
+                 FILE_ATTRIBUTE_NORMAL,  // normal file\r
+                 NULL                    // no attr. template\r
+                 );\r
+\r
+  if (FileHandle == INVALID_HANDLE_VALUE) { \r
+      EnterCriticalSection (&mCriticalSection);\r
+      Error (NULL, 0, 0, NULL, "could not open file %s", LogFile);\r
+      LeaveCriticalSection (&mCriticalSection);\r
+      return 1;\r
+  }\r
+  \r
+  //\r
+  // Init ProcInfo and StartInfo\r
+  //\r
+  ZeroMemory (&ProcInfo, sizeof (PROCESS_INFORMATION));\r
+  ZeroMemory (&StartInfo, sizeof (STARTUPINFO));\r
+  StartInfo.cb         = sizeof (STARTUPINFO); \r
+  StartInfo.hStdError  = FileHandle;\r
+  StartInfo.hStdOutput = FileHandle;\r
+  StartInfo.hStdInput  = GetStdHandle (STD_INPUT_HANDLE);\r
+  StartInfo.dwFlags    = STARTF_USESTDHANDLES;\r
+\r
+  //\r
+  // Create the child process\r
+  //\r
+  FuncRetn = CreateProcess (\r
+               NULL,          // no application name\r
+               BuildCmd,      // command line \r
+               NULL,          // process security attributes \r
+               NULL,          // primary thread security attributes \r
+               TRUE,          // handles are inherited \r
+               0,             // creation flags \r
+               NULL,          // use parent's environment \r
+               WorkingDir,    // set current directory \r
+               &StartInfo,    // STARTUPINFO pointer \r
+               &ProcInfo      // receives PROCESS_INFORMATION \r
+               );\r
+  \r
+  if (FuncRetn == FALSE) {\r
+    EnterCriticalSection (&mCriticalSection);\r
+    Error (NULL, 0, 0, NULL, "could not create child process");\r
+    LeaveCriticalSection (&mCriticalSection);\r
+    CloseHandle (FileHandle);\r
+    return 1;\r
+  } \r
+  \r
+  //\r
+  // Wait until child process exits\r
+  //\r
+  WaitForSingleObject (ProcInfo.hProcess, INFINITE);\r
+  GetExitCodeProcess (ProcInfo.hProcess, &ExitCode);\r
+  CloseHandle (ProcInfo.hProcess);\r
+  CloseHandle (ProcInfo.hThread);\r
+  CloseHandle (FileHandle);\r
+  \r
+  if (ExitCode != 0) {\r
+    return 1;\r
+  } else {\r
+    return 0;\r
+  }\r
+}\r
+\r
+//\r
+// Thread function\r
+//\r
+static DWORD WINAPI\r
+ThreadProc (\r
+  LPVOID lpParam\r
+  )\r
+{\r
+  UINT32      ThreadId;\r
+  BUILD_ITEM  *PreviousBuildItem;\r
+  BUILD_ITEM  *CurrentBuildItem;\r
+  BUILD_ITEM  *NextBuildItem;\r
+  INT8        WorkingDir[MAX_PATH];  \r
+  INT8        LogFile[MAX_PATH];\r
+  INT8        BuildCmd[MAX_PATH];\r
+  \r
+  ThreadId = (UINT32)lpParam;\r
+  //\r
+  // Loop until error occurred or no more build items available for build\r
+  //\r
+  for (;;) {\r
+    WaitForSingleObject (mSemaphoreHandle, INFINITE);\r
+    if (mError || mDone) {\r
+      return 0;\r
+    }\r
+    \r
+    //\r
+    // When code runs here, there must have one build item available for this \r
+    // thread. Loop until error occurred or get one build item for build.\r
+    //\r
+    for (;;) {\r
+      EnterCriticalSection (&mCriticalSection);\r
+      PreviousBuildItem = NULL;\r
+      CurrentBuildItem  = mWaitingList;\r
+      while (CurrentBuildItem != NULL) {\r
+        NextBuildItem = CurrentBuildItem->Next;\r
+        //\r
+        // CheckSourceFile() is to avoid concurrently build the same source file\r
+        // which may cause the muti-thread build failure\r
+        //\r
+        if (CheckSourceFile (CurrentBuildItem->SourceFileList)) {\r
+          //\r
+          // Move the current build item from mWaitingList\r
+          //\r
+          if (PreviousBuildItem != NULL) {\r
+            PreviousBuildItem->Next = NextBuildItem;\r
+          } else {\r
+            mWaitingList = NextBuildItem;\r
+          }\r
+          //\r
+          // Add the current build item to the head of mBuildingList\r
+          //\r
+          CurrentBuildItem->Next = mBuildingList;\r
+          mBuildingList = CurrentBuildItem;\r
+          //\r
+          // If no more build items is pending or waiting for build,\r
+          // wake up every child thread for exit.\r
+          //\r
+          if ((mPendingList == NULL) && (mWaitingList == NULL)) {\r
+            mDone = 1;\r
+            //\r
+            // Make sure to wake up every child thread for exit\r
+            //        \r
+            ReleaseSemaphore (mSemaphoreHandle, mThreadNumber, NULL);\r
+          }\r
+          break;\r
+        }\r
+        PreviousBuildItem = CurrentBuildItem;\r
+        CurrentBuildItem  = NextBuildItem;\r
+      }\r
+      if (CurrentBuildItem != NULL) {\r
+        //\r
+        // Display build item info\r
+        //\r
+        printf ("\t[Thread_%d] nmake -nologo -f %s all\n", ThreadId, CurrentBuildItem->Makefile);\r
+        //\r
+        // Prepare build task\r
+        //\r
+        sprintf (WorkingDir, "%s\\%s", mBuildDir, CurrentBuildItem->Processor);\r
+        sprintf (LogFile, "%s\\%s_%s_%d.txt", mLogDir, CurrentBuildItem->BaseName, \r
+                 CurrentBuildItem->Processor, CurrentBuildItem->Index);\r
+        sprintf (BuildCmd, "nmake -nologo -f %s all", CurrentBuildItem->Makefile);\r
+        LeaveCriticalSection (&mCriticalSection);\r
+        break;\r
+      } else {\r
+        LeaveCriticalSection (&mCriticalSection);\r
+        //\r
+        // All the build items in mWaitingList have source file conflict with \r
+        // mBuildingList. This rarely hapeens. Need wait for the build items in\r
+        // mBuildingList to be finished by other child threads.\r
+        //\r
+        Sleep (1000);\r
+        if (mError) {\r
+          return 0;\r
+        }\r
+      }\r
+    }\r
+    \r
+    //\r
+    // Start to build the CurrentBuildItem\r
+    //\r
+    if (RunBuildTask (WorkingDir, LogFile, BuildCmd)) {\r
+      //\r
+      // Build failure\r
+      //\r
+      mError = 1;\r
+      //\r
+      // Make sure to wake up every child thread for exit\r
+      //\r
+      ReleaseSemaphore (mSemaphoreHandle, mThreadNumber, NULL);\r
+      SetEvent(mEventHandle);\r
+\r
+      return mError;\r
+    } else {\r
+      //\r
+      // Build success\r
+      //\r
+      CurrentBuildItem->CompleteFlag = 1;\r
+      \r
+      EnterCriticalSection (&mCriticalSection);\r
+      //\r
+      // Move this build item from mBuildingList\r
+      //\r
+      if (mBuildingList == CurrentBuildItem) {\r
+        mBuildingList = mBuildingList->Next;\r
+      } else {\r
+        NextBuildItem = mBuildingList;\r
+        while (NextBuildItem->Next != CurrentBuildItem) {\r
+          NextBuildItem = NextBuildItem->Next;\r
+        }\r
+        NextBuildItem->Next = CurrentBuildItem->Next;\r
+      }\r
+      //\r
+      // Add this build item to mDoneList\r
+      //\r
+      CurrentBuildItem->Next = mDoneList;\r
+      mDoneList = CurrentBuildItem;\r
+      LeaveCriticalSection (&mCriticalSection);\r
+      \r
+      SetEvent(mEventHandle);\r
+    }\r
+  }\r
+}\r
+\r
+INT8\r
+StartMultiThreadBuild (\r
+  BUILD_ITEM  **BuildList,\r
+  UINT32      ThreadNumber,\r
+  INT8        *BuildDir\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Start multi-thread build for a specified build list\r
+\r
+Arguments:\r
+  \r
+  BuildList     - build list for multi-thread build\r
+  ThreadNumber  - thread number for multi-thread build\r
+  BuildDir      - build dir\r
+\r
+Returns:\r
+\r
+  0             - Successfully finished the multi-thread build\r
+  other value   - Build failure\r
+\r
+--*/\r
+{\r
+  UINT32        Index;\r
+  UINT32        Count;\r
+  BUILD_ITEM    *PreviousBuildItem;\r
+  BUILD_ITEM    *CurrentBuildItem;\r
+  BUILD_ITEM    *NextBuildItem;\r
+  HANDLE        *ThreadHandle;\r
+  INT8          Cmd[MAX_PATH];\r
+  \r
+  mError        = 0;\r
+  mDone         = 0;\r
+  mThreadNumber = ThreadNumber;\r
+  mBuildDir     = BuildDir;\r
+  mPendingList  = *BuildList;\r
+  *BuildList    = NULL;\r
+  mWaitingList  = NULL;\r
+  mBuildingList = NULL;\r
+  mDoneList     = NULL;\r
+  \r
+  //\r
+  // Do nothing when mPendingList is empty\r
+  //\r
+  if (mPendingList == NULL) {\r
+    return 0;\r
+  }\r
+  \r
+  //\r
+  // Get build item count of mPendingList\r
+  //\r
+  Count = 0;\r
+  CurrentBuildItem = mPendingList;\r
+  while (CurrentBuildItem != NULL) {\r
+    Count++;\r
+    CurrentBuildItem = CurrentBuildItem->Next;\r
+  }\r
+  \r
+  //\r
+  // The semaphore is also used to wake up child threads for exit,\r
+  // so need to make sure "maximum count" >= "thread number".\r
+  //\r
+  if (Count < ThreadNumber) {\r
+    Count = ThreadNumber;\r
+  }\r
+  \r
+  //\r
+  // Init mSemaphoreHandle\r
+  //\r
+  mSemaphoreHandle = CreateSemaphore (\r
+                       NULL,       // default security attributes\r
+                       0,          // initial count\r
+                       Count,      // maximum count\r
+                       NULL        // unnamed semaphore\r
+                       );\r
+  if (mSemaphoreHandle == NULL) {\r
+    Error (NULL, 0, 0, NULL, "failed to create semaphore");\r
+    RestoreBuildList (BuildList);\r
+    return 1;\r
+  }  \r
+\r
+  //\r
+  // Init mEventHandle\r
+  //\r
+  mEventHandle = CreateEvent( \r
+                   NULL,     // default security attributes\r
+                   FALSE,    // auto-reset event\r
+                   TRUE,     // initial state is signaled\r
+                   NULL      // object not named\r
+                   ); \r
+  if (mEventHandle == NULL) { \r
+    Error (NULL, 0, 0, NULL, "failed to create event");\r
+    CloseHandle (mSemaphoreHandle);\r
+    RestoreBuildList (BuildList);\r
+    return 1;\r
+  }\r
+  \r
+  //\r
+  // Init mCriticalSection\r
+  //\r
+  InitializeCriticalSection (&mCriticalSection);\r
+  \r
+  //\r
+  // Create build item log dir\r
+  //\r
+  sprintf (mLogDir, "%s\\Log", mBuildDir);\r
+  _mkdir (mLogDir);\r
+  \r
+  //\r
+  // Create child threads for muti-thread build\r
+  //\r
+  ThreadHandle = malloc (ThreadNumber * sizeof (HANDLE));\r
+  if (ThreadHandle == NULL) {\r
+    Error (NULL, 0, 0, NULL, "failed to allocate memory");\r
+    CloseHandle (mSemaphoreHandle);\r
+    CloseHandle (mEventHandle);\r
+    RestoreBuildList (BuildList);\r
+    return 1;\r
+  }\r
+  for (Index = 0; Index < ThreadNumber; Index++) {\r
+    ThreadHandle[Index] = CreateThread (\r
+                            NULL,           // default security attributes\r
+                            0,              // use default stack size\r
+                            ThreadProc,     // thread function\r
+                            (LPVOID)Index,  // argument to thread function: use Index as thread id\r
+                            0,              // use default creation flags\r
+                            NULL            // thread identifier not needed\r
+                            );\r
+    if (ThreadHandle[Index] == NULL) {\r
+      Error (NULL, 0, 0, NULL, "failed to create Thread_%d", Index);\r
+      mError       = 1;\r
+      ThreadNumber = Index;\r
+      //\r
+      // Make sure to wake up every child thread for exit\r
+      //\r
+      ReleaseSemaphore (mSemaphoreHandle, ThreadNumber, NULL);\r
+      break;\r
+    }\r
+  }\r
+  \r
+  //\r
+  // Loop until error occurred or no more build items pending for build\r
+  //\r
+  for (;;) {\r
+    WaitForSingleObject (mEventHandle, INFINITE);\r
+    if (mError) {\r
+      break;\r
+    }\r
+    Count = 0;\r
+    \r
+    EnterCriticalSection (&mCriticalSection);\r
+    PreviousBuildItem = NULL;\r
+    CurrentBuildItem  = mPendingList;\r
+    while (CurrentBuildItem != NULL) {\r
+      NextBuildItem = CurrentBuildItem->Next;\r
+      if (CheckDependency (CurrentBuildItem->DependencyList)) {\r
+        //\r
+        // Move the current build item from mPendingList\r
+        //\r
+        if (PreviousBuildItem != NULL) {\r
+          PreviousBuildItem->Next = NextBuildItem;\r
+        } else {\r
+          mPendingList = NextBuildItem;\r
+        }\r
+        //\r
+        // Add the current build item to the head of mWaitingList\r
+        //\r
+        CurrentBuildItem->Next = mWaitingList;\r
+        mWaitingList = CurrentBuildItem;\r
+        Count++;\r
+      } else {\r
+        PreviousBuildItem = CurrentBuildItem;\r
+      }\r
+      CurrentBuildItem  = NextBuildItem;\r
+    }\r
+    LeaveCriticalSection (&mCriticalSection);\r
+    \r
+    ReleaseSemaphore (mSemaphoreHandle, Count, NULL);\r
+    if (mPendingList == NULL) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Wait until all threads have terminated\r
+  //\r
+  WaitForMultipleObjects (ThreadNumber, ThreadHandle, TRUE, INFINITE);\r
+  \r
+  if (mError && (mBuildingList != NULL)) {\r
+    //\r
+    // Dump build failure log of the first build item which doesn't finish the build\r
+    //\r
+    printf ("\tnmake -nologo -f %s all\n", mBuildingList->Makefile);\r
+    sprintf (Cmd, "type %s\\%s_%s_%d.txt 2>NUL", mLogDir, mBuildingList->BaseName,\r
+             mBuildingList->Processor, mBuildingList->Index);\r
+    _flushall ();\r
+    if (system (Cmd)) {\r
+      Error (NULL, 0, 0, NULL, "failed to run \"%s\"", Cmd);\r
+    }\r
+  }\r
+\r
+  DeleteCriticalSection (&mCriticalSection);\r
+  for (Index = 0; Index < ThreadNumber; Index++) {\r
+    CloseHandle (ThreadHandle[Index]);\r
+  }\r
+  free (ThreadHandle);\r
+  CloseHandle (mSemaphoreHandle);\r
+  CloseHandle (mEventHandle);\r
+  RestoreBuildList (BuildList);\r
+\r
+  return mError;\r
+}\r