public FilteredDirectedGraph(IReadonlyDirectedGraph graph, VisitationTracker nodeFilter) { m_graph = graph; m_nodeFilter = nodeFilter; m_nodePredicate = node => nodeFilter.WasVisited(node); m_edgePredicate = edge => nodeFilter.WasVisited(edge.OtherNode); m_nodeHeights = Lazy.Create(ComputeHeights); }
/// <inheritdoc /> public GraphPatchingStatistics PartiallyReloadGraph(HashSet <AbsolutePath> affectedSpecs) { Contract.Requires(affectedSpecs != null); var startTime = DateTime.UtcNow; var mustSkipDueToTransitivity = new VisitationTracker(m_oldPipGraph); var mustPostponeDueToServicePips = new VisitationTracker(m_oldPipGraph); var toPostpone = new SortedList <uint, Pip>(); MultiValueDictionary <int, NodeId> nodesByHeight = m_oldPipGraph.TopSort(); int numReloadedPips = 0; int numNotReloadablePips = 0; m_builder.SealDirectoryTable.StartPatching(); // go one level at a time: // - process nodes at the same height in parallel // - postpone service-related pips because they must be added in the order of creation // (because, for example, pip builder adds forward edges from service client to service finalization pips) for (int height = 0; height < nodesByHeight.Count; height++) { Parallel.ForEach( nodesByHeight[height], new ParallelOptions { MaxDegreeOfParallelism = m_maxDegreeOfParallelism }, body: (node) => { var pipId = node.ToPipId(); var pipType = m_oldPipTable.GetPipType(pipId); // skip non-reloadable pips if (!s_reloadablePipTypes.Contains(pipType)) { Interlocked.Increment(ref numNotReloadablePips); return; } var pip = HydratePip(pipId); AbsolutePath?producingSpec = GetProducerSpecFile(pip); // check if this node must be skipped due to its spec file being affected if (producingSpec == null || affectedSpecs.Contains(producingSpec.Value)) { mustSkipDueToTransitivity.MarkVisited(node); MarkAllDependentsVisited(mustSkipDueToTransitivity, node); return; } // check if this pip is a service-related pip which must be postponed if (ServicePipKindUtil.IsServiceRelatedPip(pip)) { SynchronizedAddToSortedList(toPostpone, pip); MarkAllDependentsVisited(mustPostponeDueToServicePips, node); return; } // check if this node must be postponed because it depends on a node that was already postponed if (mustPostponeDueToServicePips.WasVisited(node)) { SynchronizedAddToSortedList(toPostpone, pip); MarkAllDependentsVisited(mustPostponeDueToServicePips, node); return; } // check if this node must be skipped due to transitivity ThrowIfVisited(mustSkipDueToTransitivity, pip); // everything passed: reload this node ReloadPip(pip, ref numReloadedPips); }); } // add postponed nodes sequentially in the order of creation foreach (var pip in toPostpone.Values) { var serviceKind = ServicePipKindUtil.ServiceKind(pip); if (serviceKind != ServicePipKind.ServiceShutdown && serviceKind != ServicePipKind.ServiceFinalization) { // 'shutdown' and 'finalization' are exception because of forward edges that are added // during construction from service client pips to service shutdown/finalization pips. ThrowIfVisited(mustSkipDueToTransitivity, pip); } ReloadPip(pip, ref numReloadedPips); } m_builder.SealDirectoryTable.FinishPatching(); return(new GraphPatchingStatistics { ElapsedMilliseconds = (int)DateTime.UtcNow.Subtract(startTime).TotalMilliseconds, NumPipsReloaded = numReloadedPips, NumPipsAutomaticallyAdded = m_builder.PipCount - numReloadedPips, NumPipsNotReloadable = numNotReloadablePips, NumPipsSkipped = mustSkipDueToTransitivity.VisitedCount, AffectedSpecs = affectedSpecs, }); }
/// <inheritdoc /> public bool ContainsNode(NodeId node) { return(m_nodeFilter.WasVisited(node)); }