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
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.
18 This module is used to add multi-thread build support to ProcessDsc utility
19 to improve the build performance.
29 #include "MultiThread.h"
33 BUILD_ITEM
**BuildList
,
42 Add a build item to a specified build list
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
53 Pointer to the newly added build item
57 BUILD_ITEM
*NewBuildItem
;
60 // Create a new build item
62 NewBuildItem
= malloc (sizeof (BUILD_ITEM
));
63 if (NewBuildItem
== NULL
) {
66 memset (NewBuildItem
, 0, sizeof (BUILD_ITEM
));
67 NewBuildItem
->BaseName
= _strdup (BaseName
);
68 NewBuildItem
->Processor
= _strdup (Processor
);
69 NewBuildItem
->Makefile
= _strdup (Makefile
);
72 // Add the build item to the head of the build list
74 NewBuildItem
->Next
= *BuildList
;
75 *BuildList
= NewBuildItem
;
82 BUILD_ITEM
*BuildItem
,
89 Add a source file for a build item
93 BuildItem - build item to add the source file
94 FileName - source file name to be added
98 Pointer to the newly added source file item
102 SOURCE_FILE_ITEM
*NewSourceFile
;
105 // Create a new source file item
107 NewSourceFile
= malloc (sizeof (SOURCE_FILE_ITEM
));
108 if (NewSourceFile
== NULL
) {
111 memset (NewSourceFile
, 0, sizeof (SOURCE_FILE_ITEM
));
112 NewSourceFile
->FileName
= _strdup (FileName
);
115 // Add the source file item to the head of the source file list
117 NewSourceFile
->Next
= BuildItem
->SourceFileList
;
118 BuildItem
->SourceFileList
= NewSourceFile
;
120 return NewSourceFile
;
125 BUILD_ITEM
*BuildList
,
126 BUILD_ITEM
*BuildItem
,
134 Add a build dependency for a build item in the specified build list
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
145 Pointer to the newly added build dependency
149 BUILD_ITEM
*TempBuildItem
;
150 DEPENDENCY_ITEM
*NewDependency
;
153 // Search the dependency in the build list
155 TempBuildItem
= BuildList
;
156 while (TempBuildItem
!= NULL
) {
157 if ((_stricmp (TempBuildItem
->BaseName
, BaseName
) == 0) &&
158 (_stricmp (TempBuildItem
->Processor
, BuildItem
->Processor
) == 0) &&
159 (TempBuildItem
!= BuildItem
)) {
162 TempBuildItem
= TempBuildItem
->Next
;
164 if (TempBuildItem
== NULL
) {
169 // This index is used to isolate two modules with same base name and processor.
170 // (ProcessDsc allows duplicate base name libraries.)
173 BuildItem
->Index
= TempBuildItem
->Index
+ 1;
177 // Create a new build dependency item
179 NewDependency
= malloc (sizeof (DEPENDENCY_ITEM
));
180 if (NewDependency
== NULL
) {
183 memset (NewDependency
, 0, sizeof (DEPENDENCY_ITEM
));
184 NewDependency
->Dependency
= TempBuildItem
;
187 // Add the build dependency item to the head of the dependency list
189 NewDependency
->Next
= BuildItem
->DependencyList
;
190 BuildItem
->DependencyList
= NewDependency
;
192 return NewDependency
;
197 BUILD_ITEM
*BuildList
207 BuildList - build list to be freed
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
;
220 TempBuildItem
= BuildList
;
221 while (TempBuildItem
!= NULL
) {
222 free (TempBuildItem
->BaseName
);
223 free (TempBuildItem
->Processor
);
224 free (TempBuildItem
->Makefile
);
227 // Free source file list
229 TempSourceFile
= TempBuildItem
->SourceFileList
;
230 while (TempSourceFile
!= NULL
) {
231 FreeSourceFile
= TempSourceFile
;
232 TempSourceFile
= TempSourceFile
->Next
;
233 free (FreeSourceFile
);
237 // Free dependency list
239 TempDependency
= TempBuildItem
->DependencyList
;
240 while (TempDependency
!= NULL
) {
241 FreeDependency
= TempDependency
;
242 TempDependency
= TempDependency
->Next
;
243 free (FreeDependency
);
246 FreeBuildItem
= TempBuildItem
;
247 TempBuildItem
= TempBuildItem
->Next
;
248 free (FreeBuildItem
);
254 COMPONENTS_ITEM
**ComponentsList
260 Add a new components item to a specified components list
264 ComponentsList - components list where the new components item will be added
268 Pointer to the newly added components item
272 COMPONENTS_ITEM
*NewComponents
;
273 COMPONENTS_ITEM
*TempComponents
;
276 // Create a new components item
278 NewComponents
= malloc (sizeof (COMPONENTS_ITEM
));
279 if (NewComponents
== NULL
) {
282 memset (NewComponents
, 0, sizeof (COMPONENTS_ITEM
));
285 // Add the components item to the tail of the components list
287 TempComponents
= *ComponentsList
;
288 if (TempComponents
== NULL
) {
289 *ComponentsList
= NewComponents
;
291 while (TempComponents
->Next
!= NULL
) {
292 TempComponents
= TempComponents
->Next
;
294 TempComponents
->Next
= NewComponents
;
297 return NewComponents
;
302 COMPONENTS_ITEM
*ComponentsList
308 Free a components list
312 ComponentsList - components list to be freed
318 COMPONENTS_ITEM
*TempComponents
;
319 COMPONENTS_ITEM
*FreeComponents
;
321 TempComponents
= ComponentsList
;
322 while (TempComponents
!= NULL
) {
323 FreeBuildList (TempComponents
->BuildList
);
324 FreeComponents
= TempComponents
;
325 TempComponents
= TempComponents
->Next
;
326 free (FreeComponents
);
331 // Module globals for multi-thread build
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
347 // Restore the BuildList (not care about the sequence of the build items)
351 BUILD_ITEM
**BuildList
354 BUILD_ITEM
*TempBuildItem
;
356 if (mPendingList
!= NULL
) {
358 // Add the mPendingList to the header of *BuildList
360 TempBuildItem
= mPendingList
;
361 while (TempBuildItem
->Next
!= NULL
) {
362 TempBuildItem
= TempBuildItem
->Next
;
364 TempBuildItem
->Next
= *BuildList
;
365 *BuildList
= mPendingList
;
368 if (mWaitingList
!= NULL
) {
370 // Add the mWaitingList to the header of *BuildList
372 TempBuildItem
= mWaitingList
;
373 while (TempBuildItem
->Next
!= NULL
) {
374 TempBuildItem
= TempBuildItem
->Next
;
376 TempBuildItem
->Next
= *BuildList
;
377 *BuildList
= mWaitingList
;
380 if (mBuildingList
!= NULL
) {
382 // Add the mBuildingList to the header of *BuildList
384 TempBuildItem
= mBuildingList
;
385 while (TempBuildItem
->Next
!= NULL
) {
386 TempBuildItem
= TempBuildItem
->Next
;
388 TempBuildItem
->Next
= *BuildList
;
389 *BuildList
= mBuildingList
;
392 if (mDoneList
!= NULL
) {
394 // Add the mDoneList to the header of *BuildList
396 TempBuildItem
= mDoneList
;
397 while (TempBuildItem
->Next
!= NULL
) {
398 TempBuildItem
= TempBuildItem
->Next
;
400 TempBuildItem
->Next
= *BuildList
;
401 *BuildList
= mDoneList
;
406 // Return non-zero when no source file build conflict
410 SOURCE_FILE_ITEM
*SourceFileList
413 BUILD_ITEM
*TempBuildItem
;
414 SOURCE_FILE_ITEM
*TempSourceFile
;
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) {
424 TempSourceFile
= TempSourceFile
->Next
;
426 TempBuildItem
= TempBuildItem
->Next
;
428 SourceFileList
= SourceFileList
->Next
;
435 // Return non-zero when all the dependency build items has been built
439 DEPENDENCY_ITEM
*DependencyList
442 while (DependencyList
!= NULL
) {
443 if (!(DependencyList
->Dependency
->CompleteFlag
)) {
446 DependencyList
= DependencyList
->Next
;
453 // Run the build task. The system() function call will cause stdout conflict
454 // in multi-thread envroment, so implement this through CreateProcess().
464 SECURITY_ATTRIBUTES SecAttr
;
465 PROCESS_INFORMATION ProcInfo
;
466 STARTUPINFO StartInfo
;
473 SecAttr
.nLength
= sizeof (SECURITY_ATTRIBUTES
);
474 SecAttr
.bInheritHandle
= TRUE
;
475 SecAttr
.lpSecurityDescriptor
= NULL
;
478 // Create the log file
480 FileHandle
= CreateFile (
481 LogFile
, // file to create
482 GENERIC_WRITE
, // open for writing
484 &SecAttr
, // can be inherited by child processes
485 CREATE_ALWAYS
, // overwrite existing
486 FILE_ATTRIBUTE_NORMAL
, // normal file
487 NULL
// no attr. template
490 if (FileHandle
== INVALID_HANDLE_VALUE
) {
491 EnterCriticalSection (&mCriticalSection
);
492 Error (NULL
, 0, 0, NULL
, "could not open file %s", LogFile
);
493 LeaveCriticalSection (&mCriticalSection
);
498 // Init ProcInfo and StartInfo
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
;
509 // Create the child process
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
518 NULL
, // use parent's environment
519 WorkingDir
, // set current directory
520 &StartInfo
, // STARTUPINFO pointer
521 &ProcInfo
// receives PROCESS_INFORMATION
524 if (FuncRetn
== FALSE
) {
525 EnterCriticalSection (&mCriticalSection
);
526 Error (NULL
, 0, 0, NULL
, "could not create child process");
527 LeaveCriticalSection (&mCriticalSection
);
528 CloseHandle (FileHandle
);
533 // Wait until child process exits
535 WaitForSingleObject (ProcInfo
.hProcess
, INFINITE
);
536 GetExitCodeProcess (ProcInfo
.hProcess
, &ExitCode
);
537 CloseHandle (ProcInfo
.hProcess
);
538 CloseHandle (ProcInfo
.hThread
);
539 CloseHandle (FileHandle
);
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
];
564 ThreadId
= (UINT32
)lpParam
;
566 // Loop until error occurred or no more build items available for build
569 WaitForSingleObject (mSemaphoreHandle
, INFINITE
);
570 if (mError
|| mDone
) {
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.
579 EnterCriticalSection (&mCriticalSection
);
580 PreviousBuildItem
= NULL
;
581 CurrentBuildItem
= mWaitingList
;
582 while (CurrentBuildItem
!= NULL
) {
583 NextBuildItem
= CurrentBuildItem
->Next
;
585 // CheckSourceFile() is to avoid concurrently build the same source file
586 // which may cause the muti-thread build failure
588 if (CheckSourceFile (CurrentBuildItem
->SourceFileList
)) {
590 // Move the current build item from mWaitingList
592 if (PreviousBuildItem
!= NULL
) {
593 PreviousBuildItem
->Next
= NextBuildItem
;
595 mWaitingList
= NextBuildItem
;
598 // Add the current build item to the head of mBuildingList
600 CurrentBuildItem
->Next
= mBuildingList
;
601 mBuildingList
= CurrentBuildItem
;
603 // If no more build items is pending or waiting for build,
604 // wake up every child thread for exit.
606 if ((mPendingList
== NULL
) && (mWaitingList
== NULL
)) {
609 // Make sure to wake up every child thread for exit
611 ReleaseSemaphore (mSemaphoreHandle
, mThreadNumber
, NULL
);
615 PreviousBuildItem
= CurrentBuildItem
;
616 CurrentBuildItem
= NextBuildItem
;
618 if (CurrentBuildItem
!= NULL
) {
620 // Display build item info
622 printf ("\t[Thread_%d] nmake -nologo -f %s all\n", ThreadId
, CurrentBuildItem
->Makefile
);
624 // Prepare build task
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
);
633 LeaveCriticalSection (&mCriticalSection
);
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.
647 // Start to build the CurrentBuildItem
649 if (RunBuildTask (WorkingDir
, LogFile
, BuildCmd
)) {
655 // Make sure to wake up every child thread for exit
657 ReleaseSemaphore (mSemaphoreHandle
, mThreadNumber
, NULL
);
658 SetEvent(mEventHandle
);
665 CurrentBuildItem
->CompleteFlag
= 1;
667 EnterCriticalSection (&mCriticalSection
);
669 // Move this build item from mBuildingList
671 if (mBuildingList
== CurrentBuildItem
) {
672 mBuildingList
= mBuildingList
->Next
;
674 NextBuildItem
= mBuildingList
;
675 while (NextBuildItem
->Next
!= CurrentBuildItem
) {
676 NextBuildItem
= NextBuildItem
->Next
;
678 NextBuildItem
->Next
= CurrentBuildItem
->Next
;
681 // Add this build item to mDoneList
683 CurrentBuildItem
->Next
= mDoneList
;
684 mDoneList
= CurrentBuildItem
;
685 LeaveCriticalSection (&mCriticalSection
);
687 SetEvent(mEventHandle
);
693 StartMultiThreadBuild (
694 BUILD_ITEM
**BuildList
,
702 Start multi-thread build for a specified build list
706 BuildList - build list for multi-thread build
707 ThreadNumber - thread number for multi-thread build
712 0 - Successfully finished the multi-thread build
713 other value - Build failure
719 BUILD_ITEM
*PreviousBuildItem
;
720 BUILD_ITEM
*CurrentBuildItem
;
721 BUILD_ITEM
*NextBuildItem
;
722 HANDLE
*ThreadHandle
;
727 mThreadNumber
= ThreadNumber
;
728 mBuildDir
= BuildDir
;
729 mPendingList
= *BuildList
;
732 mBuildingList
= NULL
;
736 // Do nothing when mPendingList is empty
738 if (mPendingList
== NULL
) {
743 // Get build item count of mPendingList
746 CurrentBuildItem
= mPendingList
;
747 while (CurrentBuildItem
!= NULL
) {
749 CurrentBuildItem
= CurrentBuildItem
->Next
;
753 // The semaphore is also used to wake up child threads for exit,
754 // so need to make sure "maximum count" >= "thread number".
756 if (Count
< ThreadNumber
) {
757 Count
= ThreadNumber
;
761 // Init mSemaphoreHandle
763 mSemaphoreHandle
= CreateSemaphore (
764 NULL
, // default security attributes
766 Count
, // maximum count
767 NULL
// unnamed semaphore
769 if (mSemaphoreHandle
== NULL
) {
770 Error (NULL
, 0, 0, NULL
, "failed to create semaphore");
771 RestoreBuildList (BuildList
);
778 mEventHandle
= CreateEvent(
779 NULL
, // default security attributes
780 FALSE
, // auto-reset event
781 TRUE
, // initial state is signaled
782 NULL
// object not named
784 if (mEventHandle
== NULL
) {
785 Error (NULL
, 0, 0, NULL
, "failed to create event");
786 CloseHandle (mSemaphoreHandle
);
787 RestoreBuildList (BuildList
);
792 // Init mCriticalSection
794 InitializeCriticalSection (&mCriticalSection
);
797 // Create build item log dir
799 sprintf (mLogDir
, "%s\\Log", mBuildDir
);
803 // Create child threads for muti-thread build
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
);
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
822 if (ThreadHandle
[Index
] == NULL
) {
823 Error (NULL
, 0, 0, NULL
, "failed to create Thread_%d", Index
);
825 ThreadNumber
= Index
;
827 // Make sure to wake up every child thread for exit
829 ReleaseSemaphore (mSemaphoreHandle
, ThreadNumber
, NULL
);
835 // Loop until error occurred or no more build items pending for build
838 WaitForSingleObject (mEventHandle
, INFINITE
);
844 EnterCriticalSection (&mCriticalSection
);
845 PreviousBuildItem
= NULL
;
846 CurrentBuildItem
= mPendingList
;
847 while (CurrentBuildItem
!= NULL
) {
848 NextBuildItem
= CurrentBuildItem
->Next
;
849 if (CheckDependency (CurrentBuildItem
->DependencyList
)) {
851 // Move the current build item from mPendingList
853 if (PreviousBuildItem
!= NULL
) {
854 PreviousBuildItem
->Next
= NextBuildItem
;
856 mPendingList
= NextBuildItem
;
859 // Add the current build item to the head of mWaitingList
861 CurrentBuildItem
->Next
= mWaitingList
;
862 mWaitingList
= CurrentBuildItem
;
865 PreviousBuildItem
= CurrentBuildItem
;
867 CurrentBuildItem
= NextBuildItem
;
869 LeaveCriticalSection (&mCriticalSection
);
871 ReleaseSemaphore (mSemaphoreHandle
, Count
, NULL
);
872 if (mPendingList
== NULL
) {
878 // Wait until all threads have terminated
880 WaitForMultipleObjects (ThreadNumber
, ThreadHandle
, TRUE
, INFINITE
);
882 if (mError
&& (mBuildingList
!= NULL
)) {
884 // Dump build failure log of the first build item which doesn't finish the build
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
);
891 Error (NULL
, 0, 0, NULL
, "failed to run \"%s\"", Cmd
);
895 DeleteCriticalSection (&mCriticalSection
);
896 for (Index
= 0; Index
< ThreadNumber
; Index
++) {
897 CloseHandle (ThreadHandle
[Index
]);
900 CloseHandle (mSemaphoreHandle
);
901 CloseHandle (mEventHandle
);
902 RestoreBuildList (BuildList
);