private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) { using (Logger.LogBlock(FunctionId.FindReference_ProcessAsync, _cancellationToken)) { // quick exit if (projectToDocumentMap.Count == 0) { return; } // Add a progress item for each (document, symbol, finder) set that we will execute. // We'll mark the item as completed in "ProcessDocumentAsync". var totalFindCount = projectToDocumentMap.Sum( kvp1 => kvp1.Value.Sum(kvp2 => kvp2.Value.Count)); await _progressTracker.AddItemsAsync(totalFindCount).ConfigureAwait(false); using var _ = ArrayBuilder <Task> .GetInstance(out var tasks); foreach (var(project, documentMap) in projectToDocumentMap) { tasks.Add(Task.Factory.StartNew(() => ProcessProjectAsync(project, documentMap), _cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } await Task.WhenAll(tasks).ConfigureAwait(false); } }
private async Task ProcessProjectAsync( Project project, ProjectToDocumentMap projectToDocumentMap) { if (!projectToDocumentMap.TryGetValue(project, out var documentMap)) { // No files in this project to process. We can bail here. We'll have cached our // compilation if there are any projects left to process that depend on us. return; } projectToDocumentMap.Remove(project); // Now actually process the project. await ProcessProjectAsync(project, documentMap).ConfigureAwait(false); }
private async Task ProcessProjectsAsync( IEnumerable<ProjectId> connectedProjectSet, ProjectToDocumentMap projectToDocumentMap) { var visitedProjects = new HashSet<ProjectId>(); // Make sure we process each project in the set. Process each project in depth first // order. That way when we process a project, the compilations for all projects that it // depends on will have been created already. foreach (var projectId in connectedProjectSet) { _cancellationToken.ThrowIfCancellationRequested(); await ProcessProjectAsync( projectId, projectToDocumentMap, visitedProjects).ConfigureAwait(false); } }
private static void ValidateProjectToDocumentMap(ProjectToDocumentMap projectToDocumentMap) { var set = new HashSet <(SymbolGroup group, ISymbol symbol, IReferenceFinder finder)>(); foreach (var documentMap in projectToDocumentMap.Values) { foreach (var documentToFinderList in documentMap) { set.Clear(); foreach (var tuple in documentToFinderList.Value) { Debug.Assert(set.Add(tuple)); } } } }
private static void ValidateProjectToDocumentMap( ProjectToDocumentMap projectToDocumentMap) { var set = new HashSet <(SymbolAndProjectId symbolAndProjectId, IReferenceFinder finder)>(); foreach (var documentMap in projectToDocumentMap.Values) { foreach (var documentToFinderList in documentMap) { set.Clear(); foreach (var finder in documentToFinderList.Value) { Contract.Requires(set.Add(finder)); } } } }
private async Task<ProjectToDocumentMap> CreateProjectToDocumentMapAsync(ProjectMap projectMap) { using (Logger.LogBlock(FunctionId.FindReference_CreateDocumentMapAsync, _cancellationToken)) { var finalMap = new ProjectToDocumentMap(); foreach (var kvp in projectMap) { var project = kvp.Key; var projectQueue = kvp.Value; var documentMap = new DocumentMap(); foreach (var symbolAndFinder in projectQueue) { _cancellationToken.ThrowIfCancellationRequested(); var symbolAndProjectId = symbolAndFinder.symbolAndProjectId; var symbol = symbolAndProjectId.Symbol; var finder = symbolAndFinder.finder; var documents = await finder.DetermineDocumentsToSearchAsync(symbol, project, _documents, _cancellationToken).ConfigureAwait(false); foreach (var document in documents.Distinct().WhereNotNull()) { if (_documents == null || _documents.Contains(document)) { documentMap.Add(document, symbolAndFinder); } } } Contract.ThrowIfTrue(documentMap.Any(kvp1 => kvp1.Value.Count != kvp1.Value.ToSet().Count)); if (documentMap.Count > 0) { finalMap.Add(project, documentMap); } } return finalMap; } }
private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap) { using (Logger.LogBlock(FunctionId.FindReference_ProcessAsync, _cancellationToken)) { // quick exit if (projectToDocumentMap.Count == 0) { return; } // Get the connected components of the dependency graph and process each individually. // That way once a component is done we can throw away all the memory associated with // it. // For each connected component, we'll process the individual projects from bottom to // top. i.e. we'll first process the projects with no dependencies. Then the projects // that depend on those projects, and so and. This way we always have creates the // dependent compilations when they're needed by later projects. If we went the other // way (i.e. processed the projects with lots of project dependencies first), then we'd // have to create all their depedent compilations in order to get their compilation. // This would be very expensive and would take a lot of time before we got our first // result. var connectedProjects = _dependencyGraph.GetDependencySets(_cancellationToken); // Add a progress item for each (document, symbol, finder) set that we will execute. // We'll mark the item as completed in "ProcessDocumentAsync". var totalFindCount = projectToDocumentMap.Sum( kvp1 => kvp1.Value.Sum(kvp2 => kvp2.Value.Count)); await _progressTracker.AddItemsAsync(totalFindCount).ConfigureAwait(false); // Now, go through each connected project set and process it independently. foreach (var connectedProjectSet in connectedProjects) { _cancellationToken.ThrowIfCancellationRequested(); await ProcessProjectsAsync( connectedProjectSet, projectToDocumentMap).ConfigureAwait(false); } } }
private async Task ProcessProjectAsync( ProjectId projectId, ProjectToDocumentMap projectToDocumentMap, HashSet<ProjectId> visitedProjects) { // Don't visit projects more than once. if (visitedProjects.Add(projectId)) { var project = _solution.GetProject(projectId); // Visit dependencies first. That way the compilation for a project that we depend // on is already ready for us when we need it. foreach (var dependent in project.ProjectReferences) { _cancellationToken.ThrowIfCancellationRequested(); await ProcessProjectAsync( dependent.ProjectId, projectToDocumentMap, visitedProjects).ConfigureAwait(false); } await ProcessProjectAsync(project, projectToDocumentMap).ConfigureAwait(false); } }
private async Task <ProjectToDocumentMap> CreateProjectToDocumentMapAsync(ProjectMap projectMap, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.FindReference_CreateDocumentMapAsync, cancellationToken)) { using var _ = ArrayBuilder <Task <(ImmutableArray <Document>, ISymbol)> > .GetInstance(out var tasks); foreach (var(project, projectQueue) in projectMap) { foreach (var symbol in projectQueue) { tasks.Add(Task.Factory.StartNew(() => DetermineDocumentsToSearchAsync(project, symbol, cancellationToken), cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } } var results = await Task.WhenAll(tasks).ConfigureAwait(false); var finalMap = new ProjectToDocumentMap(); foreach (var(documents, symbol) in results) { foreach (var document in documents) { finalMap.GetOrAdd(document.Project, s_createDocumentMap) .MultiAdd(document, symbol); } } #if DEBUG foreach (var(project, documentMap) in finalMap) { Contract.ThrowIfTrue(documentMap.Any(kvp1 => kvp1.Value.Count != kvp1.Value.ToSet().Count)); } #endif return(finalMap); } }