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