--- /dev/null
+/*++\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