Example #1
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);
                }
            }
        }