Example #1
0
 /// <summary>
 /// Event handler for the BuildEngine's OnNewRequest event.
 /// </summary>
 private void OnNewRequest(BuildRequestBlocker blocker)
 {
     if (_nodeEndpoint.LinkStatus == LinkStatus.Active)
     {
         _nodeEndpoint.SendData(blocker);
     }
 }
Example #2
0
        public void TestSimpleRequestWithCachedResultsFail()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request = CreateBuildRequest(1, 1, new string[] { "foo" });
            BuildResult result = CacheBuildResult(request, "foo", TestUtilities.GetStopWithErrorResult());

            BuildRequestBlocker blocker = new BuildRequestBlocker(request.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(2, response.Count);

            // First response tells the parent of the results.
            Assert.Equal(ScheduleActionType.ReportResults, response[0].Action);
            Assert.True(ResultsCache_Tests.AreResultsIdentical(result, response[0].Unblocker.Result));

            // Second response tells the parent to continue.
            Assert.Equal(ScheduleActionType.ResumeExecution, response[1].Action);
            Assert.Null(response[1].Unblocker.Result);
        }
Example #3
0
        public void TestThreeRequestsWithOneFailure()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" });
            BuildRequest request2 = CreateBuildRequest(2, 1, new string[] { "bar" });
            BuildResult result2 = CacheBuildResult(request2, "bar", TestUtilities.GetStopWithErrorResult());
            BuildRequest request3 = CreateBuildRequest(3, 1, new string[] { "baz" });

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2, request3 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(2, response.Count);
            Assert.True(ResultsCache_Tests.AreResultsIdentical(result2, response[0].Unblocker.Result));
            Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[1].Action);
        }
Example #4
0
        public void TestMultipleRequestsWithSomeResults()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" });
            CreateConfiguration(2, "bar.proj");
            BuildRequest request2 = CreateBuildRequest(2, 2, new string[] { "bar" });
            BuildResult result2 = CacheBuildResult(request2, "bar", TestUtilities.GetSuccessResult());

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.AreEqual(2, response.Count);
            Assert.AreEqual(ScheduleActionType.ReportResults, response[0].Action);
            Assert.IsTrue(ResultsCache_Tests.AreResultsIdentical(result2, response[0].Unblocker.Result));
            Assert.AreEqual(ScheduleActionType.ScheduleWithConfiguration, response[1].Action);
            Assert.AreEqual(request1, response[1].BuildRequest);
        }
Example #5
0
        public void TestNoNewNodesCreatedForMultipleRequestsWithSameConfiguration()
        {
            _host.BuildParameters.MaxNodeCount = 3;

            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" });
            BuildRequest request2 = CreateBuildRequest(2, 1, new string[] { "bar" });
            BuildRequest request3 = CreateBuildRequest(3, 1, new string[] { "baz" });
            BuildRequest request4 = CreateBuildRequest(4, 1, new string[] { "qux" });

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2, request3, request4 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(1, response.Count);
            Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[0].Action);
            Assert.Equal(request1, response[0].BuildRequest);
        }
Example #6
0
        public void TestMaxNodeCountNodesNotExceededWithSomeOOPRequests2()
        {
            _host.BuildParameters.MaxNodeCount = 3;

            CreateConfiguration(1, "foo.proj");
            CreateConfiguration(2, "bar.proj");
            CreateConfiguration(3, "baz.proj");
            CreateConfiguration(4, "quz.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request2 = CreateBuildRequest(2, 2, new string[] { "bar" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request3 = CreateBuildRequest(3, 3, new string[] { "baz" });
            BuildRequest request4 = CreateBuildRequest(4, 4, new string[] { "qux" });

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2, request3, request4 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(2, response.Count);
            Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[0].Action);
            Assert.Equal(request3, response[0].BuildRequest);
            Assert.Equal(ScheduleActionType.CreateNode, response[1].Action);
            Assert.Equal(NodeAffinity.OutOfProc, response[1].RequiredNodeType);
            Assert.Equal(2, response[1].NumberOfNodesToCreate);
        }
Example #7
0
 /// <summary>
 /// Raises the OnNewRequest event.
 /// </summary>
 /// <param name="blocker">Information about what is blocking the current request.</param>
 private void RaiseRequestBlocked(BuildRequestBlocker blocker)
 {
     RequestBlockedDelegate requestBlocked = OnRequestBlocked;
     if (null != requestBlocked)
     {
         requestBlocked(blocker);
     }
 }
Example #8
0
        public void TestMultipleRequestsWithAllResults()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" });
            BuildResult result1 = CacheBuildResult(request1, "foo", TestUtilities.GetSuccessResult());
            CreateConfiguration(2, "bar.proj");
            BuildRequest request2 = CreateBuildRequest(2, 2, new string[] { "bar" });
            BuildResult result2 = CacheBuildResult(request2, "bar", TestUtilities.GetSuccessResult());

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(3, response.Count);

            // First two are the results which were cached.
            Assert.Equal(ScheduleActionType.ReportResults, response[0].Action);
            Assert.True(ResultsCache_Tests.AreResultsIdentical(result1, response[0].Unblocker.Result));
            Assert.Equal(ScheduleActionType.ReportResults, response[1].Action);
            Assert.True(ResultsCache_Tests.AreResultsIdentical(result2, response[1].Unblocker.Result));

            // Last response is to continue the parent.
            Assert.Equal(ScheduleActionType.ResumeExecution, response[2].Action);
            Assert.Equal(request1.ParentGlobalRequestId, response[2].Unblocker.BlockedRequestId);
            Assert.Null(response[2].Unblocker.Result);
        }
Example #9
0
        /// <summary>
        /// Sends a build request to the Build Manager for scheduling
        /// </summary>
        /// <param name="blocker">The information about why the request is blocked.</param>
        private void IssueBuildRequest(BuildRequestBlocker blocker)
        {
            ErrorUtilities.VerifyThrowArgumentNull(blocker, "blocker");

            if (blocker.BuildRequests == null)
            {
                // This is the case when we aren't blocking on new requests, but rather an in-progress request which is executing a target for which we need results.
                TraceEngine("Blocking global request {0} on global request {1} because it is already executing target {2}", blocker.BlockedRequestId, blocker.BlockingRequestId, blocker.BlockingTarget);
            }
            else
            {
                foreach (BuildRequest blockingRequest in blocker.BuildRequests)
                {
                    TraceEngine("Sending node request {0} (configuration {1}) with parent {2} to Build Manager", blockingRequest.NodeRequestId, blockingRequest.ConfigurationId, blocker.BlockedRequestId);
                }
            }

            RaiseRequestBlocked(blocker);
        }
Example #10
0
        /// <summary>
        /// Reports a configuration response to the request, allowing it to satisfy outstanding requests.
        /// <seealso cref="BuildRequestConfigurationResponse"/>
        /// </summary>
        /// <param name="response">The configuration response.</param>
        /// <remarks>
        /// Called by the Node.  Non-overlapping with other calls from the Node.
        /// </remarks>
        public void ReportConfigurationResponse(BuildRequestConfigurationResponse response)
        {
            QueueAction(
                () =>
                {
                    ErrorUtilities.VerifyThrow(_status != BuildRequestEngineStatus.Shutdown && _status != BuildRequestEngineStatus.Uninitialized, "Engine loop not yet started, status is {0}.", _status);

                    TraceEngine("Received configuration response for node config {0}, now global config {1}.", response.NodeConfigurationId, response.GlobalConfigurationId);
                    ErrorUtilities.VerifyThrow(_componentHost != null, "No host object set");

                    // Remove the unresolved configuration entry from the unresolved cache.
                    BuildRequestConfiguration config = _unresolvedConfigurations[response.NodeConfigurationId];
                    _unresolvedConfigurations.RemoveConfiguration(response.NodeConfigurationId);

                    // Add the configuration to the resolved cache unless it already exists there.  This will be
                    // the case in single-proc mode as we share the global cache with the Build Manager.
                    IConfigCache globalConfigurations = (IConfigCache)_componentHost.GetComponent(BuildComponentType.ConfigCache);
                    if (!globalConfigurations.HasConfiguration(response.GlobalConfigurationId))
                    {
                        config.ConfigurationId = response.GlobalConfigurationId;
                        config.ResultsNodeId = response.ResultsNodeId;
                        globalConfigurations.AddConfiguration(config);
                    }

                    // Evaluate the current list of requests and tell any waiting requests about our new configuration update.
                    // If any requests can now issue build requests, do so.
                    IResultsCache resultsCache = (IResultsCache)_componentHost.GetComponent(BuildComponentType.ResultsCache);

                    List<BuildRequestBlocker> blockersToIssue = new List<BuildRequestBlocker>();
                    foreach (BuildRequestEntry currentEntry in _requests)
                    {
                        List<BuildRequest> requestsToIssue = new List<BuildRequest>();
                        if (currentEntry.State == BuildRequestEntryState.Waiting)
                        {
                            // Resolve the configuration id and get the list of requests to be issued, if any.
                            bool issueRequests = currentEntry.ResolveConfigurationRequest(response.NodeConfigurationId, response.GlobalConfigurationId);

                            // If we had any requests which are now ready to be issued, do so.
                            if (issueRequests)
                            {
                                IEnumerable<BuildRequest> resolvedRequests = currentEntry.GetRequestsToIssueIfReady();
                                foreach (BuildRequest request in resolvedRequests)
                                {
                                    // If we have results already in the cache for this request, give them to the
                                    // entry now.
                                    ResultsCacheResponse cacheResponse = resultsCache.SatisfyRequest(request, config.ProjectInitialTargets, config.ProjectDefaultTargets, config.GetAfterTargetsForDefaultTargets(request), skippedResultsAreOK: false);
                                    if (cacheResponse.Type == ResultsCacheResponseType.Satisfied)
                                    {
                                        // We have a result, give it back to this request.
                                        currentEntry.ReportResult(cacheResponse.Results);
                                    }
                                    else
                                    {
                                        requestsToIssue.Add(request);
                                    }
                                }
                            }
                        }

                        if (requestsToIssue.Count != 0)
                        {
                            BuildRequestBlocker blocker = new BuildRequestBlocker(currentEntry.Request.GlobalRequestId, currentEntry.GetActiveTargets(), requestsToIssue.ToArray());
                            blockersToIssue.Add(blocker);
                        }
                    }

                    // Issue all of the outstanding build requests.
                    foreach (BuildRequestBlocker blocker in blockersToIssue)
                    {
                        // Issue the build request
                        IssueBuildRequest(blocker);
                    }
                },
                isLastTask: false);
        }
Example #11
0
        /// <summary>
        /// This method is responsible for evaluating whether we have enough information to make the request of the Build Manager,
        /// or if we need to obtain additional configuration information.  It then issues either configuration
        /// requests or build requests, or both as needed.
        /// </summary>
        /// <param name="issuingEntry">The BuildRequestEntry which is making the request</param>
        /// <param name="newRequests">The array of "child" build requests to be issued.</param>
        /// <remarks>
        /// When we receive a build request, we first have to determine if we already have a configuration which matches the
        /// one used by the request.  We do this because everywhere we deal with requests and results beyond this function, we
        /// use configuration ids, which are assigned once by the Build Manager and are global to the system.  If we do
        /// not have a global configuration id, we can't check to see if we already have build results for the request, so we 
        /// cannot send the request out.  Thus, first we determine the configuration id.
        /// 
        /// Assuming we don't have the global configuration id locally, we will send the configuration to the Build Manager.
        /// It will look up or assign the global configuration id and send it back to us.
        /// 
        /// Once we have the global configuration id, we can then look up results locally.  If we have enough results to fulfill
        /// the request, we give them back to the request, otherwise we have to forward the request to the Build Mangager
        /// for scheduling.
        /// </remarks>
        private void IssueBuildRequests(BuildRequestEntry issuingEntry, FullyQualifiedBuildRequest[] newRequests)
        {
            ErrorUtilities.VerifyThrow(_componentHost != null, "No host object set");

            // For each request, we need to determine if we have a local configuration in the
            // configuration cache.  If we do, we can issue the build request immediately.
            // Otherwise, we need to ask the Build Manager for configuration IDs and issue those requests
            // later.
            IConfigCache globalConfigCache = (IConfigCache)_componentHost.GetComponent(BuildComponentType.ConfigCache);

            // We are going to potentially issue several requests.  We don't want the state of the request being modified by
            // other threads while this occurs, so we lock the request for the duration.  This lock is the same lock
            // used by the BuildRequestEntry itself to lock each of its data-modifying methods, effectively preventing
            // any other thread from modifying the BuildRequestEntry while we hold it.  This mechanism also means that it
            // is not necessary for other threads to take the global lock explicitly if they are just doing single operations
            // to the entry rather than a series of them.
            lock (issuingEntry.GlobalLock)
            {
                List<BuildResult> existingResultsToReport = new List<BuildResult>();
                HashSet<NGen<int>> unresolvedConfigurationsAdded = new HashSet<NGen<int>>();

                foreach (FullyQualifiedBuildRequest request in newRequests)
                {
                    // Do we have a matching configuration?
                    BuildRequestConfiguration matchingConfig = globalConfigCache.GetMatchingConfiguration(request.Config);
                    BuildRequest newRequest = null;
                    if (matchingConfig == null)
                    {
                        // No configuration locally, are we already waiting for it?
                        matchingConfig = _unresolvedConfigurations.GetMatchingConfiguration(request.Config);
                        if (matchingConfig == null)
                        {
                            // Not waiting for it
                            request.Config.ConfigurationId = GetNextUnresolvedConfigurationId();
                            _unresolvedConfigurations.AddConfiguration(request.Config);
                            unresolvedConfigurationsAdded.Add(request.Config.ConfigurationId);
                        }
                        else
                        {
                            request.Config.ConfigurationId = matchingConfig.ConfigurationId;
                        }

                        // Whether we are already waiting for a configuration or we need to wait for another one
                        // we will add this request as waiting for a configuration.  As new configuration resolutions
                        // come in, we will check our requests which are waiting for configurations move them to
                        // waiting for results.  It is important that we tell the issuing request to wait for a result
                        // prior to issuing any necessary configuration request so that we don't get into a state where
                        // we receive the configuration response before we enter the wait state.
                        newRequest = new BuildRequest(issuingEntry.Request.SubmissionId, GetNextBuildRequestId(), request.Config.ConfigurationId, request.Targets, issuingEntry.Request.HostServices, issuingEntry.Request.BuildEventContext, issuingEntry.Request);

                        issuingEntry.WaitForResult(newRequest);

                        if (matchingConfig == null)
                        {
                            // Issue the config resolution request
                            TraceEngine("Request {0}({1}) (nr {2}) is waiting on configuration {3} (IBR)", issuingEntry.Request.GlobalRequestId, issuingEntry.Request.ConfigurationId, issuingEntry.Request.NodeRequestId, request.Config.ConfigurationId);
                            issuingEntry.WaitForConfiguration(request.Config);
                        }
                    }
                    else
                    {
                        // We have a configuration, see if we already have results locally.
                        newRequest = new BuildRequest(issuingEntry.Request.SubmissionId, GetNextBuildRequestId(), matchingConfig.ConfigurationId, request.Targets, issuingEntry.Request.HostServices, issuingEntry.Request.BuildEventContext, issuingEntry.Request);

                        IResultsCache resultsCache = (IResultsCache)_componentHost.GetComponent(BuildComponentType.ResultsCache);
                        ResultsCacheResponse response = resultsCache.SatisfyRequest(newRequest, matchingConfig.ProjectInitialTargets, matchingConfig.ProjectDefaultTargets, matchingConfig.GetAfterTargetsForDefaultTargets(newRequest), skippedResultsAreOK: false);
                        if (response.Type == ResultsCacheResponseType.Satisfied)
                        {
                            // We have a result, give it back to this request.
                            issuingEntry.WaitForResult(newRequest);

                            // Log the fact that we handled this from the cache.
                            _nodeLoggingContext.LogRequestHandledFromCache(newRequest, _configCache[newRequest.ConfigurationId], response.Results);

                            // Can't report the result directly here, because that could cause the request to go from
                            // Waiting to Ready.
                            existingResultsToReport.Add(response.Results);
                        }
                        else
                        {
                            // No result, to wait for it.
                            issuingEntry.WaitForResult(newRequest);
                        }
                    }
                }

                // If we have any results we had to report, do so now.
                foreach (BuildResult existingResult in existingResultsToReport)
                {
                    issuingEntry.ReportResult(existingResult);
                }

                // Issue any configuration requests we may still need.
                List<BuildRequestConfiguration> unresolvedConfigurationsToIssue = issuingEntry.GetUnresolvedConfigurationsToIssue();
                if (unresolvedConfigurationsToIssue != null)
                {
                    foreach (BuildRequestConfiguration unresolvedConfigurationToIssue in unresolvedConfigurationsToIssue)
                    {
                        unresolvedConfigurationsAdded.Remove(unresolvedConfigurationToIssue.ConfigurationId);
                        IssueConfigurationRequest(unresolvedConfigurationToIssue);
                    }
                }

                // Remove any configurations we ended up not waiting for, otherwise future requests will think we are still waiting for them
                // and will never get submitted.
                foreach (int unresolvedConfigurationId in unresolvedConfigurationsAdded)
                {
                    _unresolvedConfigurations.RemoveConfiguration(unresolvedConfigurationId);
                }

                // Finally, if we can issue build requests, do so.
                List<BuildRequest> requestsToIssue = issuingEntry.GetRequestsToIssueIfReady();
                if (requestsToIssue != null)
                {
                    BuildRequestBlocker blocker = new BuildRequestBlocker(issuingEntry.Request.GlobalRequestId, issuingEntry.GetActiveTargets(), requestsToIssue.ToArray());
                    IssueBuildRequest(blocker);
                }

                if (issuingEntry.State == BuildRequestEntryState.Ready)
                {
                    ErrorUtilities.VerifyThrow((requestsToIssue == null) || (requestsToIssue.Count == 0), "Entry shouldn't be ready if we also issued requests.");
                    ActivateBuildRequest(issuingEntry);
                }
            }
        }
Example #12
0
 /// <summary>
 /// Gets called by the build request engine when there is a new build request (engine callback)
 /// </summary>
 /// <param name="request"></param>
 private void RequestEngine_OnNewRequest(BuildRequestBlocker blocker)
 {
     if (this.testDataProvider != null)
     {
         foreach (BuildRequest request in blocker.BuildRequests)
         {
             this.testDataProvider.NewRequest = request;
         }
     }
 }
 /// <summary>
 /// Callback for event raised when a new build request is generated by an MSBuild callback
 /// </summary>
 /// <param name="request">The new build request</param>
 private void Engine_NewRequest(BuildRequestBlocker blocker)
 {
     _newRequest_Request = blocker;
     _newRequestEvent.Set();
 }
Example #14
0
        public void TestChildRequest()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request = CreateBuildRequest(1, 1, new string[] { "foo" });

            BuildRequestBlocker blocker = new BuildRequestBlocker(-1, new string[] { }, new BuildRequest[] { request });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            CreateConfiguration(2, "bar.proj");
            BuildRequest childRequest = CreateBuildRequest(2, 2, new string[] { "foo" }, request);
            BuildResult childResult = CacheBuildResult(childRequest, "foo", TestUtilities.GetSuccessResult());

            blocker = new BuildRequestBlocker(0, new string[] { "foo" }, new BuildRequest[] { childRequest });
            response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(2, response.Count);

            // The first response will be to report the results back to the node.
            Assert.Equal(ScheduleActionType.ReportResults, response[0].Action);
            Assert.True(ResultsCache_Tests.AreResultsIdentical(childResult, response[0].Unblocker.Result));

            // The second response will be to continue building the original request.
            Assert.Equal(ScheduleActionType.ResumeExecution, response[1].Action);
            Assert.Null(response[1].Unblocker.Result);
        }
Example #15
0
 /// <summary>
 /// Event handler for the BuildEngine's OnNewRequest event.
 /// </summary>
 private void OnNewRequest(BuildRequestBlocker blocker)
 {
     if (_nodeEndpoint.LinkStatus == LinkStatus.Active)
     {
         _nodeEndpoint.SendData(blocker);
     }
 }
Example #16
0
        public void TestMultipleRequests()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" });
            BuildRequest request2 = CreateBuildRequest(2, 1, new string[] { "bar" });

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.Equal(1, response.Count);
            Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[0].Action);
            Assert.Equal(request1, response[0].BuildRequest);
        }
Example #17
0
        /// <summary>
        /// Marks the request as being blocked by another request which is currently building a target whose results we need to proceed.
        /// </summary>
        private void HandleRequestBlockedOnInProgressTarget(SchedulableRequest blockedRequest, BuildRequestBlocker blocker)
        {
            ErrorUtilities.VerifyThrowArgumentNull(blockedRequest, "blockedRequest");
            ErrorUtilities.VerifyThrowArgumentNull(blocker, "blocker");

            // We are blocked on an in-progress request building a target whose results we need.
            SchedulableRequest blockingRequest = _schedulingData.GetScheduledRequest(blocker.BlockingRequestId);

            // The request we blocked on couldn't have been executing (because we are) so it must either be yielding (which is ok because
            // it isn't modifying its own state, just running a background process), ready, or still blocked.
            blockingRequest.VerifyOneOfStates(new SchedulableRequestState[] { SchedulableRequestState.Yielding, SchedulableRequestState.Ready, SchedulableRequestState.Blocked });

            blockedRequest.BlockByRequest(blockingRequest, blocker.TargetsInProgress);
        }
Example #18
0
        public void TestOutOfProcNodeCreatedWhenAffinityIsOutOfProc()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request2 = CreateBuildRequest(2, 1, new string[] { "bar" }, NodeAffinity.OutOfProc, _defaultParentRequest);

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            // Parent request is blocked by the fact that both child requests require the out-of-proc node that doesn't 
            // exist yet.
            Assert.Equal(1, response.Count);
            Assert.Equal(ScheduleActionType.CreateNode, response[0].Action);
            Assert.Equal(NodeAffinity.OutOfProc, response[0].RequiredNodeType);
            Assert.Equal(1, response[0].NumberOfNodesToCreate);
        }
Example #19
0
        /// <summary>
        /// Marks the request as being blocked by new requests whose results we must get before we can proceed.
        /// </summary>
        private void HandleRequestBlockedByNewRequests(SchedulableRequest parentRequest, BuildRequestBlocker blocker, List<ScheduleResponse> responses)
        {
            ErrorUtilities.VerifyThrowArgumentNull(blocker, "blocker");
            ErrorUtilities.VerifyThrowArgumentNull(responses, "responses");

            // The request is waiting on new requests.
            bool abortRequestBatch = false;
            Stack<BuildRequest> requestsToAdd = new Stack<BuildRequest>(blocker.BuildRequests.Length);
            foreach (BuildRequest request in blocker.BuildRequests)
            {
                // Assign a global request id to this request.
                if (request.GlobalRequestId == BuildRequest.InvalidGlobalRequestId)
                {
                    AssignGlobalRequestId(request);
                }

                int nodeForResults = (parentRequest == null) ? InvalidNodeId : parentRequest.AssignedNode;
                TraceScheduler("Received request {0} (node request {1}) with parent {2} from node {3}", request.GlobalRequestId, request.NodeRequestId, request.ParentGlobalRequestId, nodeForResults);

                // First, determine if we have already built this request and have results for it.  If we do, we prepare the responses for it
                // directly here.  We COULD simply report these as blocking the parent request and let the scheduler pick them up later when the parent
                // comes back up as schedulable, but we prefer to send the results back immediately so this request can (potentially) continue uninterrupted.
                ScheduleResponse response = TrySatisfyRequestFromCache(nodeForResults, request, skippedResultsAreOK: false);
                if (null != response)
                {
                    TraceScheduler("Request {0} (node request {1}) satisfied from the cache.", request.GlobalRequestId, request.NodeRequestId);

                    // BuildResult result = (response.Action == ScheduleActionType.Unblock) ? response.Unblocker.Results[0] : response.BuildResult;
                    LogRequestHandledFromCache(request, response.BuildResult);
                    responses.Add(response);

                    // If we wish to implement an algorithm where the first failing request aborts the remaining request, check for
                    // overall result being failure rather than just circular dependency.  Sync with BasicScheduler.ReportResult and
                    // BuildRequestEntry.ReportResult.
                    if (response.BuildResult.CircularDependency)
                    {
                        abortRequestBatch = true;
                    }
                }
                else
                {
                    // Ensure there is no affinity mismatch between this request and a previous request of the same configuration.
                    NodeAffinity requestAffinity = GetNodeAffinityForRequest(request);
                    NodeAffinity existingRequestAffinity = NodeAffinity.Any;
                    if (requestAffinity != NodeAffinity.Any)
                    {
                        bool affinityMismatch = false;
                        int assignedNodeId = _schedulingData.GetAssignedNodeForRequestConfiguration(request.ConfigurationId);
                        if (assignedNodeId != Scheduler.InvalidNodeId)
                        {
                            if (!_availableNodes[assignedNodeId].CanServiceRequestWithAffinity(GetNodeAffinityForRequest(request)))
                            {
                                // This request's configuration has already been assigned to a node which cannot service this affinity.
                                if (_schedulingData.GetRequestsAssignedToConfigurationCount(request.ConfigurationId) == 0)
                                {
                                    // If there are no other requests already scheduled for that configuration, we can safely reassign.
                                    _schedulingData.UnassignNodeForRequestConfiguration(request.ConfigurationId);
                                }
                                else
                                {
                                    existingRequestAffinity = (_availableNodes[assignedNodeId].ProviderType == NodeProviderType.InProc) ? NodeAffinity.InProc : NodeAffinity.OutOfProc;
                                    affinityMismatch = true;
                                }
                            }
                        }
                        else if (_schedulingData.GetRequestsAssignedToConfigurationCount(request.ConfigurationId) > 0)
                        {
                            // Would any other existing requests for this configuration mismatch?
                            foreach (SchedulableRequest existingRequest in _schedulingData.GetRequestsAssignedToConfiguration(request.ConfigurationId))
                            {
                                existingRequestAffinity = GetNodeAffinityForRequest(existingRequest.BuildRequest);
                                if (existingRequestAffinity != NodeAffinity.Any && existingRequestAffinity != requestAffinity)
                                {
                                    // The existing request has an affinity which doesn't match this one, so this one could never be scheduled.
                                    affinityMismatch = true;
                                    break;
                                }
                            }
                        }

                        if (affinityMismatch)
                        {
                            BuildResult result = new BuildResult(request, new InvalidOperationException(ResourceUtilities.FormatResourceString("AffinityConflict", requestAffinity, existingRequestAffinity)));
                            response = GetResponseForResult(nodeForResults, request, result);
                            responses.Add(response);
                            continue;
                        }
                    }

                    // Now add the requests so they would naturally be picked up by the scheduler in the order they were issued,
                    // but before any other requests in the list.  This makes us prefer a depth-first traversal.
                    requestsToAdd.Push(request);
                }
            }

            // Now add any unassigned build requests.
            if (!abortRequestBatch)
            {
                if (requestsToAdd.Count == 0)
                {
                    // All of the results are being reported directly from the cache (from above), so this request can continue on its merry way.
                    if (parentRequest != null)
                    {
                        // responses.Add(new ScheduleResponse(parentRequest.AssignedNode, new BuildRequestUnblocker(parentRequest.BuildRequest.GlobalRequestId)));
                        responses.Add(ScheduleResponse.CreateResumeExecutionResponse(parentRequest.AssignedNode, parentRequest.BuildRequest.GlobalRequestId));
                    }
                }
                else
                {
                    while (requestsToAdd.Count > 0)
                    {
                        BuildRequest requestToAdd = requestsToAdd.Pop();
                        SchedulableRequest blockingRequest = _schedulingData.CreateRequest(requestToAdd, parentRequest);

                        if (parentRequest != null)
                        {
                            parentRequest.BlockByRequest(blockingRequest, blocker.TargetsInProgress);
                        }
                    }
                }
            }
        }
Example #20
0
        public void TestMaxNodeCountOOPNodesCreatedForOOPAffinitizedRequests()
        {
            _host.BuildParameters.MaxNodeCount = 3;

            CreateConfiguration(1, "foo.proj");
            CreateConfiguration(2, "bar.proj");
            CreateConfiguration(3, "baz.proj");
            CreateConfiguration(4, "quz.proj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request2 = CreateBuildRequest(2, 2, new string[] { "bar" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request3 = CreateBuildRequest(3, 3, new string[] { "baz" }, NodeAffinity.OutOfProc, _defaultParentRequest);
            BuildRequest request4 = CreateBuildRequest(4, 4, new string[] { "qux" }, NodeAffinity.OutOfProc, _defaultParentRequest);

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2, request3, request4 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            // Parent request is blocked by the fact that both child requests require the out-of-proc node that doesn't 
            // exist yet.
            Assert.Equal(1, response.Count);
            Assert.Equal(ScheduleActionType.CreateNode, response[0].Action);
            Assert.Equal(NodeAffinity.OutOfProc, response[0].RequiredNodeType);
            Assert.Equal(3, response[0].NumberOfNodesToCreate);
        }
Example #21
0
        /// <summary>
        /// Reports that the specified request has become blocked and cannot proceed.
        /// </summary>
        public IEnumerable<ScheduleResponse> ReportRequestBlocked(int nodeId, BuildRequestBlocker blocker)
        {
            _schedulingData.EventTime = DateTime.UtcNow;
            List<ScheduleResponse> responses = new List<ScheduleResponse>();

            // Get the parent, if any
            SchedulableRequest parentRequest = null;
            if (blocker.BlockedRequestId != BuildRequest.InvalidGlobalRequestId)
            {
                if (blocker.YieldAction == YieldAction.Reacquire)
                {
                    parentRequest = _schedulingData.GetYieldingRequest(blocker.BlockedRequestId);
                }
                else
                {
                    parentRequest = _schedulingData.GetExecutingRequest(blocker.BlockedRequestId);
                }
            }

            try
            {
                // We are blocked either on new requests (top-level or MSBuild task) or on an in-progress request that is
                // building a target we want to build.
                if (blocker.YieldAction != YieldAction.None)
                {
                    TraceScheduler("Request {0} on node {1} is performing yield action {2}.", blocker.BlockedRequestId, nodeId, blocker.YieldAction);
                    HandleYieldAction(parentRequest, blocker);
                }
                else if ((blocker.BlockingRequestId == blocker.BlockedRequestId) && blocker.BlockingRequestId != BuildRequest.InvalidGlobalRequestId)
                {
                    // We are blocked waiting for a transfer of results.                    
                    HandleRequestBlockedOnResultsTransfer(parentRequest, responses);
                }
                else if (blocker.BlockingRequestId != BuildRequest.InvalidGlobalRequestId)
                {
                    // We are blocked by a request executing a target for which we need results.
                    try
                    {
                        HandleRequestBlockedOnInProgressTarget(parentRequest, blocker);
                    }
                    catch (SchedulerCircularDependencyException ex)
                    {
                        TraceScheduler("Circular dependency caused by request {0}({1}) (nr {2}), parent {3}({4}) (nr {5})", ex.Request.GlobalRequestId, ex.Request.ConfigurationId, ex.Request.NodeRequestId, parentRequest.BuildRequest.GlobalRequestId, parentRequest.BuildRequest.ConfigurationId, parentRequest.BuildRequest.NodeRequestId);
                        responses.Add(ScheduleResponse.CreateCircularDependencyResponse(nodeId, parentRequest.BuildRequest, ex.Request));
                    }
                }
                else
                {
                    // We are blocked by new requests, either top-level or MSBuild task.
                    HandleRequestBlockedByNewRequests(parentRequest, blocker, responses);
                }
            }
            catch (SchedulerCircularDependencyException ex)
            {
                TraceScheduler("Circular dependency caused by request {0}({1}) (nr {2}), parent {3}({4}) (nr {5})", ex.Request.GlobalRequestId, ex.Request.ConfigurationId, ex.Request.NodeRequestId, parentRequest.BuildRequest.GlobalRequestId, parentRequest.BuildRequest.ConfigurationId, parentRequest.BuildRequest.NodeRequestId);
                responses.Add(ScheduleResponse.CreateCircularDependencyResponse(nodeId, parentRequest.BuildRequest, ex.Request));
            }

            // Now see if we can schedule requests somewhere since we 
            // a) have a new request; and
            // b) have a node which is now waiting and not doing anything.
            ScheduleUnassignedRequests(responses);
            return responses;
        }
Example #22
0
        public void TestTraversalAffinityIsInProc()
        {
            _host.BuildParameters.MaxNodeCount = 3;

            CreateConfiguration(1, "dirs.proj");
            CreateConfiguration(2, "abc.metaproj");
            BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, _defaultParentRequest);
            BuildRequest request2 = CreateBuildRequest(2, 2, new string[] { "bar" }, _defaultParentRequest);

            BuildRequestBlocker blocker = new BuildRequestBlocker(request1.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request1, request2 });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            // There will be no request to create a new node, because both of the above requests are traversals, 
            // which have an affinity of "inproc", and the inproc node already exists. 
            Assert.Equal(1, response.Count);
            Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[0].Action);
            Assert.Equal(request1, response[0].BuildRequest);
        }
Example #23
0
 /// <summary>
 /// Updates the state of a request based on its desire to yield or reacquire control of its node.
 /// </summary>
 private void HandleYieldAction(SchedulableRequest parentRequest, BuildRequestBlocker blocker)
 {
     if (blocker.YieldAction == YieldAction.Yield)
     {
         // Mark the request blocked.
         parentRequest.Yield(blocker.TargetsInProgress);
     }
     else
     {
         // Mark the request ready.
         parentRequest.Reacquire();
     }
 }
Example #24
0
        public void TestResult()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request = CreateBuildRequest(1, 1);
            BuildRequestBlocker blocker = new BuildRequestBlocker(request.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            BuildResult result = CreateBuildResult(request, "foo", TestUtilities.GetSuccessResult());
            response = new List<ScheduleResponse>(_scheduler.ReportResult(1, result));

            Assert.Equal(2, response.Count);

            // First response is reporting the results for this request to the parent
            Assert.Equal(ScheduleActionType.ReportResults, response[0].Action);

            // Second response is continuing execution of the parent
            Assert.Equal(ScheduleActionType.ResumeExecution, response[1].Action);
            Assert.Equal(request.ParentGlobalRequestId, response[1].Unblocker.BlockedRequestId);
        }
Example #25
0
        public void TestSimpleRequest()
        {
            CreateConfiguration(1, "foo.proj");
            BuildRequest request = CreateBuildRequest(1, 1);
            BuildRequestBlocker blocker = new BuildRequestBlocker(request.ParentGlobalRequestId, new string[] { }, new BuildRequest[] { request });
            List<ScheduleResponse> response = new List<ScheduleResponse>(_scheduler.ReportRequestBlocked(1, blocker));

            Assert.AreEqual(1, response.Count);
            Assert.AreEqual(ScheduleActionType.ScheduleWithConfiguration, response[0].Action);
            Assert.AreEqual(request, response[0].BuildRequest);
        }