/// <summary> /// This is called back when this request needs to issue new requests and possible wait on them. This method will /// block the builder's thread if any of the requests require us to wait for their results. /// </summary> /// <param name="requests">The list of build requests to be built.</param> /// <returns>The results, or null if the build should terminate.</returns> private async Task<BuildResult[]> StartNewBuildRequests(FullyQualifiedBuildRequest[] requests) { // Determine if we need to wait for results from any of these requests. // UNDONE: Currently we never set ResultsNeeded to anything but true. The purpose of this flag would be // to issue another top-level build request which no other request depends on, but which must finish in order for // the build to be considered complete. This would be brand new semantics. bool waitForResults = false; foreach (FullyQualifiedBuildRequest request in requests) { if (request.ResultsNeeded) { waitForResults = true; break; } } _blockType = BlockType.BlockedOnChildRequests; // Save the current operating environment, if necessary if (waitForResults) { SaveOperatingEnvironment(); } // Issue the requests to the engine RaiseOnNewBuildRequests(requests); // TODO: OPTIMIZATION: By returning null here, we commit to having to unwind the stack all the // way back to RequestThreadProc and then shutting down the thread before we can receive the // results and continue with them. It is not always the case that this will be desirable, however, // particularly if the results we need are immediately available. In those cases, it would be // useful to wait here for a short period in case those results become available - one second // might be enough. This means we may occasionally get more than one builder thread lying around // waiting for something to happen, but that would be short lived. At the same time it would // allow these already-available results to be utilized immediately without the unwind // semantics. // Now wait for results if we are supposed to. BuildResult[] results; if (waitForResults) { WaitHandle[] handles = new WaitHandle[] { _terminateEvent, _continueEvent }; int handle; if (IsBuilderUsingLegacyThreadingSemantics(_componentHost, _requestEntry)) { handle = RequestBuilder.WaitWithBuilderThreadStart(handles, true, _componentHost.LegacyThreadingData, _requestEntry.Request.SubmissionId); } else if (_inMSBuildCallback) { CultureInfo savedCulture = CultureInfo.CurrentCulture; CultureInfo savedUICulture = CultureInfo.CurrentUICulture; handle = await handles.ToTask(); Thread.CurrentThread.CurrentCulture = savedCulture; Thread.CurrentThread.CurrentUICulture = savedUICulture; } else { handle = WaitHandle.WaitAny(handles); } // If this is not a shutdown case, then the entry should be in the active state. if (handle == 1) { // Restore the operating environment. RestoreOperatingEnvironment(); VerifyEntryInActiveState(); } results = GetResultsForContinuation(requests, handle == 1); } else { results = new BuildResult[] { }; } ErrorUtilities.VerifyThrow(requests.Length == results.Length, "# results != # requests"); _blockType = BlockType.Unblocked; return results; }
/// <summary> /// This method is called when the current request needs to build a target which is already in progress on this configuration, but which /// is being built by another request. /// </summary> /// <param name="blockingGlobalRequestId">The id of the request on which we are blocked.</param> public async Task BlockOnTargetInProgress(int blockingGlobalRequestId, string blockingTarget) { VerifyIsNotZombie(); SaveOperatingEnvironment(); _blockType = BlockType.BlockedOnTargetInProgress; RaiseOnBlockedRequest(blockingGlobalRequestId, blockingTarget); WaitHandle[] handles = new WaitHandle[] { _terminateEvent, _continueEvent }; int handle; if (IsBuilderUsingLegacyThreadingSemantics(_componentHost, _requestEntry)) { handle = WaitHandle.WaitAny(handles); } else { handle = await handles.ToTask(); } RestoreOperatingEnvironment(); if (handle == 0) { // We've been aborted throw new BuildAbortedException(); } _blockType = BlockType.Unblocked; VerifyEntryInActiveState(); }