public void Register(Workspace workspace) { var correlationId = LogAggregator.GetNextId(); lock (_gate) { if (_documentWorkCoordinatorMap.ContainsKey(workspace)) { // already registered. return; } var coordinator = new WorkCoordinator( _listener, GetAnalyzerProviders(workspace), new Registration(correlationId, workspace, _progressReporter)); _documentWorkCoordinatorMap.Add(workspace, coordinator); } SolutionCrawlerLogger.LogRegistration(correlationId, workspace); }
private void ProcessEvents(WorkspaceChangeEventArgs args, IAsyncToken asyncToken) { SolutionCrawlerLogger.LogWorkspaceEvent(_logAggregator, (int)args.Kind); // TODO: add telemetry that record how much it takes to process an event (max, min, average and etc) switch (args.Kind) { case WorkspaceChangeKind.SolutionAdded: case WorkspaceChangeKind.SolutionChanged: case WorkspaceChangeKind.SolutionReloaded: case WorkspaceChangeKind.SolutionRemoved: case WorkspaceChangeKind.SolutionCleared: ProcessSolutionEvent(args, asyncToken); break; case WorkspaceChangeKind.ProjectAdded: case WorkspaceChangeKind.ProjectChanged: case WorkspaceChangeKind.ProjectReloaded: case WorkspaceChangeKind.ProjectRemoved: ProcessProjectEvent(args, asyncToken); break; case WorkspaceChangeKind.DocumentAdded: case WorkspaceChangeKind.DocumentReloaded: case WorkspaceChangeKind.DocumentChanged: case WorkspaceChangeKind.DocumentRemoved: case WorkspaceChangeKind.AdditionalDocumentAdded: case WorkspaceChangeKind.AdditionalDocumentRemoved: case WorkspaceChangeKind.AdditionalDocumentChanged: case WorkspaceChangeKind.AdditionalDocumentReloaded: ProcessDocumentEvent(args, asyncToken); break; default: throw ExceptionUtilities.UnexpectedValue(args.Kind); } }
private async Task ProcessProjectAsync(ImmutableArray <IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationTokenSource source) { if (this.CancellationToken.IsCancellationRequested) { return; } // we do have work item for this project var projectId = workItem.ProjectId; var processedEverything = false; var processingSolution = this.Processor.CurrentSolution; try { using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessProjectAsync, w => w.ToString(), workItem, source.Token)) { var cancellationToken = source.Token; var project = processingSolution.GetProject(projectId); if (project != null) { var reasons = workItem.InvocationReasons; var semanticsChanged = reasons.Contains(PredefinedInvocationReasons.SemanticChanged) || reasons.Contains(PredefinedInvocationReasons.SolutionRemoved); using (Processor.EnableCaching(project.Id)) { await RunAnalyzersAsync(analyzers, project, (a, p, c) => a.AnalyzeProjectAsync(p, semanticsChanged, reasons, c), cancellationToken).ConfigureAwait(false); } } else { SolutionCrawlerLogger.LogProcessProjectNotExist(this.Processor._logAggregator); RemoveProject(projectId); } if (!cancellationToken.IsCancellationRequested) { processedEverything = true; } } } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } finally { // we got cancelled in the middle of processing the project. // let's make sure newly enqueued work item has all the flag needed. // Avoid retry attempts after cancellation is requested, since work will not be processed // after that point. if (!processedEverything && !CancellationToken.IsCancellationRequested) { _workItemQueue.AddOrReplace(workItem.Retry(this.Listener.BeginAsyncOperation("ReenqueueWorkItem"))); } SolutionCrawlerLogger.LogProcessProject(this.Processor._logAggregator, projectId.Id, processedEverything); // remove one that is finished running _workItemQueue.MarkWorkItemDoneFor(projectId); } }
private async Task ProcessDocumentAsync(ImmutableArray <IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationTokenSource source) { if (this.CancellationToken.IsCancellationRequested) { return; } var processedEverything = false; var documentId = workItem.DocumentId; // we should always use solution snapshot after workitem is removed from the queue. // otherwise, we can have a race such as below. // // 1.solution crawler picked up a solution // 2.before processing the solution, an workitem got changed // 3.and then the work item got picked up from the queue // 4.and use the work item with the solution that got picked up in step 1 // // step 2 is happening because solution has changed, but step 4 used old solution from step 1 // that doesn't have effects of the solution changes. // // solution crawler must remove the work item from the queue first and then pick up the soluton, // so that the queue gets new work item if there is any solution changes after the work item is removed // from the queue // // using later version of solution is always fine since, as long as there is new work item in the queue, // solution crawler will eventually call the last workitem with the lastest solution // making everything to catch up var solution = this.Processor.CurrentSolution; try { using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, source.Token)) { var cancellationToken = source.Token; var document = solution.GetDocument(documentId); if (document != null) { // if we are called because a document is opened, we invalidate the document so that // it can be re-analyzed. otherwise, since newly opened document has same version as before // analyzer will simply return same data back if (workItem.MustRefresh && !workItem.IsRetry) { var isOpen = document.IsOpen(); await ProcessOpenDocumentIfNeeded(analyzers, workItem, document, isOpen, cancellationToken).ConfigureAwait(false); await ProcessCloseDocumentIfNeeded(analyzers, workItem, document, isOpen, cancellationToken).ConfigureAwait(false); } // check whether we are having special reanalyze request await ProcessReanalyzeDocumentAsync(workItem, document, cancellationToken).ConfigureAwait(false); await ProcessDocumentAnalyzersAsync(document, analyzers, workItem, cancellationToken).ConfigureAwait(false); } else { SolutionCrawlerLogger.LogProcessDocumentNotExist(this.Processor._logAggregator); RemoveDocument(documentId); } if (!cancellationToken.IsCancellationRequested) { processedEverything = true; } } } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } finally { // we got cancelled in the middle of processing the document. // let's make sure newly enqueued work item has all the flag needed. // Avoid retry attempts after cancellation is requested, since work will not be processed // after that point. if (!processedEverything && !CancellationToken.IsCancellationRequested) { _workItemQueue.AddOrReplace(workItem.Retry(this.Listener.BeginAsyncOperation("ReenqueueWorkItem"))); } SolutionCrawlerLogger.LogProcessDocument(this.Processor._logAggregator, documentId.Id, processedEverything); // remove one that is finished running _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId); } }
protected override void PauseOnGlobalOperation() { SolutionCrawlerLogger.LogGlobalOperation(this.Processor._logAggregator); }