/// <summary>
        /// Processes all requests that are currently in the queue.
        /// </summary>
        private void ProcessRequests()
        {
            // Store a list of threads which are resolving SDKs
            List <Task> tasks = new List <Task>(_requests.Count);

            SdkResolverRequest item;

            while (_requests.TryDequeue(out item))
            {
                SdkResolverRequest request = item;

                // Start a thread to resolve an SDK and add it to the list of threads
                tasks.Add(Task.Run(() =>
                {
                    // Create an SdkReference from the request
                    SdkReference sdkReference = new SdkReference(request.Name, request.Version, request.MinimumVersion);

                    ILoggingService loggingService = Host.GetComponent(BuildComponentType.LoggingService) as ILoggingService;

                    // This call is usually cached so is very fast but can take longer for a new SDK that is downloaded.  Other queued threads for different SDKs will complete sooner and continue on which unblocks evaluations
                    SdkResult result = GetSdkResultAndCache(request.SubmissionId, sdkReference, new EvaluationLoggingContext(loggingService, request.BuildEventContext, request.ProjectPath), request.ElementLocation, request.SolutionPath, request.ProjectPath);

                    // Create a response
                    SdkResolverResponse response = new SdkResolverResponse(result.Path, result.Version);

                    // Get the node manager and send the response back to the node that requested the SDK
                    INodeManager nodeManager = Host.GetComponent(BuildComponentType.NodeManager) as INodeManager;

                    nodeManager.SendData(request.NodeId, response);
                }));
            }

            // Wait for all tasks to complete
            Task.WaitAll(tasks.ToArray());
        }
        /// <summary>
        /// Handles a response from the main node.
        /// </summary>
        /// <param name="response"></param>
        private void HandleResponse(SdkResolverResponse response)
        {
            // Store the last response so the awaiting thread can use it
            _lastResponse = response;

            // Signal that a response has been received
            _responseReceivedEvent.Set();
        }
        /// <inheritdoc cref="ISdkResolverService.ResolveSdk"/>
        public override string ResolveSdk(int submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, string solutionPath, string projectPath)
        {
            // Get a cached response if possible, otherwise send the request
            SdkResolverResponse response = _responseCache.GetOrAdd(
                sdk.Name,
                key => RequestSdkPathFromMainNode(submissionId, sdk, loggingContext, sdkReferenceLocation, solutionPath, projectPath));

            if (response.Version != null && !SdkResolverService.IsReferenceSameVersion(sdk, response.Version))
            {
                // MSB4240: Multiple versions of the same SDK "{0}" cannot be specified. The SDK version "{1}" already specified by "{2}" will be used and the version "{3}" will be ignored.
                loggingContext.LogWarning(null, new BuildEventFileInfo(sdkReferenceLocation), "ReferencingMultipleVersionsOfTheSameSdk", sdk.Name, response.Version, response.ElementLocation, sdk.Version);
            }

            return(response.FullPath);
        }
        private SdkResolverResponse RequestSdkPathFromMainNode(int submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, string solutionPath, string projectPath)
        {
            // Clear out the last response for good measure
            _lastResponse = null;

            // Create the SdkResolverRequest packet to send
            INodePacket packet = SdkResolverRequest.Create(submissionId, sdk, loggingContext.BuildEventContext, sdkReferenceLocation, solutionPath, projectPath);

            SendPacket(packet);

            // Wait for either the response or a shutdown event.  Either event means this thread should return
            WaitHandle.WaitAny(new WaitHandle[] { _responseReceivedEvent, ShutdownEvent });

            // Keep track of the element location of the reference
            _lastResponse.ElementLocation = sdkReferenceLocation;

            // Return the response which was set by another thread.  In the case of shutdown, it should be null.
            return(_lastResponse);
        }