// Copyright 2016 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.skyframe; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.collect.nestedset.NestedSetVisitor; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable; import com.google.devtools.build.skyframe.MemoizingEvaluator.EmittedEventState; import com.google.devtools.build.skyframe.QueryableGraph.Reason; import java.util.Map; import java.util.concurrent.ForkJoinPool; import javax.annotation.Nullable; /** * Context object holding sufficient information for {@link SkyFunctionEnvironment} to perform its * duties. Shared among all {@link SkyFunctionEnvironment} instances, which should regard this * object as a read-only collection of data. * *

Also used during cycle detection. */ class ParallelEvaluatorContext { enum EnqueueParentBehavior { ENQUEUE, SIGNAL, NO_ACTION } private final QueryableGraph graph; private final Version graphVersion; private final ImmutableMap skyFunctions; private final ExtendedEventHandler reporter; private final NestedSetVisitor replayingNestedSetEventVisitor; private final NestedSetVisitor replayingNestedSetPostableVisitor; private final boolean keepGoing; private final DirtyTrackingProgressReceiver progressReceiver; private final EventFilter storedEventFilter; private final ErrorInfoManager errorInfoManager; private final GraphInconsistencyReceiver graphInconsistencyReceiver; private final EvaluationVersionBehavior evaluationVersionBehavior; /** * The visitor managing the thread pool. Used to enqueue parents when an entry is finished, and, * during testing, to block until an exception is thrown if a node builder requests that. * Initialized after construction to avoid the overhead of the caller's creating a threadpool in * cases where it is not needed. */ private final Supplier visitorSupplier; ParallelEvaluatorContext( QueryableGraph graph, Version graphVersion, ImmutableMap skyFunctions, ExtendedEventHandler reporter, EmittedEventState emittedEventState, boolean keepGoing, final DirtyTrackingProgressReceiver progressReceiver, EventFilter storedEventFilter, ErrorInfoManager errorInfoManager, RunnableMaker runnableMaker, GraphInconsistencyReceiver graphInconsistencyReceiver, final int threadCount) { this( graph, graphVersion, skyFunctions, reporter, emittedEventState, keepGoing, progressReceiver, storedEventFilter, errorInfoManager, graphInconsistencyReceiver, () -> new NodeEntryVisitor(threadCount, progressReceiver, runnableMaker), EvaluationVersionBehavior.MAX_CHILD_VERSIONS); } ParallelEvaluatorContext( QueryableGraph graph, Version graphVersion, ImmutableMap skyFunctions, ExtendedEventHandler reporter, EmittedEventState emittedEventState, boolean keepGoing, final DirtyTrackingProgressReceiver progressReceiver, EventFilter storedEventFilter, ErrorInfoManager errorInfoManager, RunnableMaker runnableMaker, GraphInconsistencyReceiver graphInconsistencyReceiver, final ForkJoinPool forkJoinPool, EvaluationVersionBehavior evaluationVersionBehavior) { this( graph, graphVersion, skyFunctions, reporter, emittedEventState, keepGoing, progressReceiver, storedEventFilter, errorInfoManager, graphInconsistencyReceiver, () -> new NodeEntryVisitor(forkJoinPool, progressReceiver, runnableMaker), evaluationVersionBehavior); } /** * Returns a {@link Runnable} given a {@code key} to evaluate and an {@code evaluationPriority} * indicating whether it should be scheduled for evaluation soon (higher is better). The returned * {@link Runnable} is a {@link ComparableRunnable} so that it can be ordered by {@code * evaluationPriority} in a priority queue if needed. */ interface RunnableMaker { ComparableRunnable make(SkyKey key, int evaluationPriority); } interface ComparableRunnable extends Runnable, Comparable {} private ParallelEvaluatorContext( QueryableGraph graph, Version graphVersion, ImmutableMap skyFunctions, ExtendedEventHandler reporter, EmittedEventState emittedEventState, boolean keepGoing, final DirtyTrackingProgressReceiver progressReceiver, EventFilter storedEventFilter, ErrorInfoManager errorInfoManager, GraphInconsistencyReceiver graphInconsistencyReceiver, Supplier visitorSupplier, EvaluationVersionBehavior evaluationVersionBehavior) { this.graph = graph; this.graphVersion = graphVersion; this.skyFunctions = skyFunctions; this.reporter = reporter; this.graphInconsistencyReceiver = graphInconsistencyReceiver; this.evaluationVersionBehavior = evaluationVersionBehavior; this.replayingNestedSetEventVisitor = new NestedSetVisitor<>(new NestedSetEventReceiver(reporter), emittedEventState.eventState); this.replayingNestedSetPostableVisitor = new NestedSetVisitor<>( new NestedSetPostableReceiver(reporter), emittedEventState.postableState); this.keepGoing = keepGoing; this.progressReceiver = Preconditions.checkNotNull(progressReceiver); this.storedEventFilter = storedEventFilter; this.errorInfoManager = errorInfoManager; this.visitorSupplier = Suppliers.memoize(visitorSupplier); } Map getBatchValues( @Nullable SkyKey parent, Reason reason, Iterable keys) throws InterruptedException { return graph.getBatch(parent, reason, keys); } /** * Signals all parents that this node is finished. If {@code enqueueParents} is true, also * enqueues any parents that are ready. Otherwise, this indicates that we are building this node * after the main build aborted, so skip any parents that are already done (that can happen with * cycles). */ void signalValuesAndEnqueueIfReady( SkyKey skyKey, Iterable keys, Version version, EnqueueParentBehavior enqueueParents) throws InterruptedException { // No fields of the entry are needed here, since we're just enqueuing for evaluation, but more // importantly, these hints are not respected for not-done nodes. If they are, we may need to // alter this hint. Map batch = graph.getBatch(skyKey, Reason.SIGNAL_DEP, keys); switch (enqueueParents) { case ENQUEUE: for (SkyKey key : keys) { NodeEntry entry = Preconditions.checkNotNull(batch.get(key), key); if (entry.signalDep(version)) { getVisitor().enqueueEvaluation(key, Integer.MAX_VALUE); } } return; case SIGNAL: for (SkyKey key : keys) { NodeEntry entry = Preconditions.checkNotNull(batch.get(key), key); if (!entry.isDone()) { // In cycles, we can have parents that are already done. entry.signalDep(version); } } return; case NO_ACTION: return; default: throw new IllegalStateException(enqueueParents + ", " + skyKey); } } QueryableGraph getGraph() { return graph; } Version getGraphVersion() { return graphVersion; } boolean keepGoing() { return keepGoing; } NodeEntryVisitor getVisitor() { return visitorSupplier.get(); } DirtyTrackingProgressReceiver getProgressReceiver() { return progressReceiver; } GraphInconsistencyReceiver getGraphInconsistencyReceiver() { return graphInconsistencyReceiver; } NestedSetVisitor getReplayingNestedSetEventVisitor() { return replayingNestedSetEventVisitor; } NestedSetVisitor getReplayingNestedSetPostableVisitor() { return replayingNestedSetPostableVisitor; } ExtendedEventHandler getReporter() { return reporter; } ImmutableMap getSkyFunctions() { return skyFunctions; } EventFilter getStoredEventFilter() { return storedEventFilter; } ErrorInfoManager getErrorInfoManager() { return errorInfoManager; } EvaluationVersionBehavior getEvaluationVersionBehavior() { return evaluationVersionBehavior; } /** Receives the events from the NestedSet and delegates to the reporter. */ private static class NestedSetEventReceiver implements NestedSetVisitor.Receiver { private final ExtendedEventHandler reporter; public NestedSetEventReceiver(ExtendedEventHandler reporter) { this.reporter = reporter; } @Override public void accept(TaggedEvents events) { for (Event e : events.getEvents()) { reporter.handle(e); } } } /** Receives the postables from the NestedSet and delegates to the reporter. */ private static class NestedSetPostableReceiver implements NestedSetVisitor.Receiver { private final ExtendedEventHandler reporter; public NestedSetPostableReceiver(ExtendedEventHandler reporter) { this.reporter = reporter; } @Override public void accept(Postable post) { reporter.post(post); } } }