/// <summary> /// Handles a request from a remote node. /// </summary> /// <param name="node">The ID of the remote node.</param> /// <param name="request">The <see cref="SdkResolverRequest"/> containing information about the SDK to resolve.</param> /// <remarks>This method must not directly handle requests because it would block requests from other nodes. Instead, it simply /// adds requests to a queue which are processed by a background thread.</remarks> private void HandleRequest(int node, SdkResolverRequest request) { if (_requestHandler == null) { // Start the background thread which will process queued requests if it has not already been started. lock (_lockObject) { if (_requestHandler == null) { // Create the event used to signal that a request was received _requestReceivedEvent = new ManualResetEvent(initialState: false); // Create the queue used to store requests that need to be processed _requests = new ConcurrentQueue <SdkResolverRequest>(); // Create the thread which processes requests _requestHandler = Task.Factory.StartNew(RequestHandlerPumpProc, TaskCreationOptions.LongRunning); } } } // Associate the node with the request request.NodeId = node; _requests.Enqueue(request); // Signal that one or more requests have been received _requestReceivedEvent.Set(); }
/// <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(() => { SdkResult response = null; try { // 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 response = ResolveSdk ( request.SubmissionId, sdkReference, new EvaluationLoggingContext(loggingService, request.BuildEventContext, request.ProjectPath), request.ElementLocation, new SdkEnv(request.SolutionPath, request.ProjectPath), request.Interactive ); } catch (Exception e) { ILoggingService loggingService = Host.GetComponent(BuildComponentType.LoggingService) as ILoggingService; EvaluationLoggingContext loggingContext = new EvaluationLoggingContext(loggingService, request.BuildEventContext, request.ProjectPath); loggingService.LogFatalBuildError(loggingContext.BuildEventContext, e, new BuildEventFileInfo(request.ElementLocation)); } finally { // 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()); }
private SdkResult RequestSdkPathFromMainNode(int submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, string solutionPath, string projectPath, bool interactive) { // 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, interactive); 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); }