Fix threadpool leak in SkyQueryEnvironment
Shutdown the SkyQueryEnvironment's threadpool after query evaluation
is complete and the environment is ready for disposal.
--
MOS_MIGRATED_REVID=125975317
diff --git a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
index 3a8f9ba..5f8749d 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
@@ -38,6 +38,7 @@
import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.cmdline.TargetPattern;
import com.google.devtools.build.lib.collect.CompactHashSet;
+import com.google.devtools.build.lib.concurrent.ExecutorUtil;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
@@ -119,9 +120,14 @@
// TODO(janakr): Unify with RecursivePackageProviderBackedTargetPatternResolver's constant.
private static final int BATCH_CALLBACK_SIZE = 10000;
private static final int DEFAULT_THREAD_COUNT = Runtime.getRuntime().availableProcessors();
-
- protected WalkableGraph graph;
- private Supplier<ImmutableSet<PathFragment>> blacklistPatternsSupplier;
+ private static final Logger LOG = Logger.getLogger(SkyQueryEnvironment.class.getName());
+ private static final Function<Target, Label> TARGET_LABEL_FUNCTION =
+ new Function<Target, Label>() {
+ @Override
+ public Label apply(Target target) {
+ return target.getLabel();
+ }
+ };
private final BlazeTargetAccessor accessor = new BlazeTargetAccessor(this);
private final int loadingPhaseThreads;
@@ -130,39 +136,19 @@
private final String parserPrefix;
private final PathPackageLocator pkgPath;
- private static final Logger LOG = Logger.getLogger(SkyQueryEnvironment.class.getName());
-
- private static final Function<Target, Label> TARGET_LABEL_FUNCTION =
- new Function<Target, Label>() {
-
- @Override
- public Label apply(Target target) {
- return target.getLabel();
- }
- };
-
+ // Note that the executor returned by Executors.newFixedThreadPool doesn't start any threads
+ // unless work is submitted to it.
private final ListeningExecutorService threadPool =
MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(
DEFAULT_THREAD_COUNT,
- new ThreadFactoryBuilder().setNameFormat("GetPackages-%d").build()));
+ new ThreadFactoryBuilder().setNameFormat("QueryEnvironment-%d").build()));
+
+ // The following fields are set in the #beforeEvaluateQuery method.
+ protected WalkableGraph graph;
+ private Supplier<ImmutableSet<PathFragment>> blacklistPatternsSupplier;
private RecursivePackageProviderBackedTargetPatternResolver resolver;
- private static class BlacklistSupplier implements Supplier<ImmutableSet<PathFragment>> {
- private final WalkableGraph graph;
-
- BlacklistSupplier(WalkableGraph graph) {
- this.graph = graph;
- }
-
- @Override
- public ImmutableSet<PathFragment> get() {
- return ((BlacklistedPackagePrefixesValue)
- graph.getValue(BlacklistedPackagePrefixesValue.key()))
- .getPatterns();
- }
- }
-
public SkyQueryEnvironment(
boolean keepGoing,
int loadingPhaseThreads,
@@ -189,17 +175,19 @@
"No queries can be performed with an empty universe");
}
- private void init() throws InterruptedException {
+ private void beforeEvaluateQuery() throws InterruptedException {
EvaluationResult<SkyValue> result;
try (AutoProfiler p = AutoProfiler.logged("evaluation and walkable graph", LOG)) {
- result = graphFactory.prepareAndGet(universeScope, parserPrefix, loadingPhaseThreads,
- eventHandler);
+ result =
+ graphFactory.prepareAndGet(
+ universeScope, parserPrefix, loadingPhaseThreads, eventHandler);
}
- graph = result.getWalkableGraph();
+ SkyKey universeKey = graphFactory.getUniverseKey(universeScope, parserPrefix);
+ checkEvaluationResult(result, universeKey);
+ graph = result.getWalkableGraph();
blacklistPatternsSupplier = Suppliers.memoize(new BlacklistSupplier(graph));
- SkyKey universeKey = graphFactory.getUniverseKey(universeScope, parserPrefix);
ImmutableList<TargetPatternKey> universeTargetPatternKeys =
PrepareDepsOfPatternsFunction.getTargetPatternKeys(
PrepareDepsOfPatternsFunction.getSkyKeys(universeKey, eventHandler));
@@ -211,23 +199,38 @@
eventHandler,
TargetPatternEvaluator.DEFAULT_FILTERING_POLICY,
threadPool);
+ }
- // The prepareAndGet call above evaluates a single PrepareDepsOfPatterns SkyKey.
- // We expect to see either a single successfully evaluated value or a cycle in the result.
+ /**
+ * The {@link EvaluationResult} is from the evaluation of a single PrepareDepsOfPatterns node. We
+ * expect to see either a single successfully evaluated value or a cycle in the result.
+ */
+ private void checkEvaluationResult(EvaluationResult<SkyValue> result, SkyKey universeKey) {
Collection<SkyValue> values = result.values();
if (!values.isEmpty()) {
- Preconditions.checkState(values.size() == 1, "Universe query \"%s\" returned multiple"
- + " values unexpectedly (%s values in result)", universeScope, values.size());
+ Preconditions.checkState(
+ values.size() == 1,
+ "Universe query \"%s\" returned multiple values unexpectedly (%s values in result)",
+ universeScope,
+ values.size());
Preconditions.checkNotNull(result.get(universeKey), result);
} else {
// No values in the result, so there must be an error. We expect the error to be a cycle.
boolean foundCycle = !Iterables.isEmpty(result.getError().getCycleInfo());
- Preconditions.checkState(foundCycle, "Universe query \"%s\" failed with non-cycle error: %s",
- universeScope, result.getError());
+ Preconditions.checkState(
+ foundCycle,
+ "Universe query \"%s\" failed with non-cycle error: %s",
+ universeScope,
+ result.getError());
}
}
@Override
+ public void close() {
+ ExecutorUtil.interruptibleShutdown(threadPool);
+ }
+
+ @Override
public QueryExpression transformParsedQuery(QueryExpression queryExpression) {
// Transform each occurrence of an expressions of the form 'rdeps(<universeScope>, <T>)' to
// 'allrdeps(<T>)'. The latter is more efficient.
@@ -270,7 +273,7 @@
// result is set to have an error iff there were errors emitted during the query, so we reset
// errors here.
eventHandler.resetErrors();
- init();
+ beforeEvaluateQuery();
// SkyQueryEnvironment batches callback invocations using a BatchStreamedCallback, created here
// so that there's one per top-level evaluateQuery call. The batch size is large enough that
@@ -651,8 +654,8 @@
protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns)
throws QueryException, TargetParsingException {
// SkyQueryEnvironment directly evaluates target patterns in #getTarget and similar methods
- // using its graph, which is prepopulated using the universeScope (see #init), so no
- // preloading of target patterns is necessary.
+ // using its graph, which is prepopulated using the universeScope (see #beforeEvaluateQuery),
+ // so no preloading of target patterns is necessary.
}
private static final Function<SkyKey, Label> SKYKEY_TO_LABEL = new Function<SkyKey, Label>() {
@@ -872,6 +875,21 @@
.build();
}
+ private static class BlacklistSupplier implements Supplier<ImmutableSet<PathFragment>> {
+ private final WalkableGraph graph;
+
+ BlacklistSupplier(WalkableGraph graph) {
+ this.graph = graph;
+ }
+
+ @Override
+ public ImmutableSet<PathFragment> get() {
+ return ((BlacklistedPackagePrefixesValue)
+ graph.getValue(BlacklistedPackagePrefixesValue.key()))
+ .getPatterns();
+ }
+ }
+
@ThreadSafe
private static class ConcurrentUniquifier implements Uniquifier<Target> {