Name all the threads.
Initialize build log FileHandler once per process.
diff --git a/src/com/facebook/buck/android/BUCK b/src/com/facebook/buck/android/BUCK
index 7be1768..9fb3aca 100644
--- a/src/com/facebook/buck/android/BUCK
+++ b/src/com/facebook/buck/android/BUCK
@@ -140,6 +140,7 @@
'//src/com/facebook/buck/util:exceptions',
'//src/com/facebook/buck/util:io',
'//src/com/facebook/buck/util:util',
+ '//src/com/facebook/buck/util/concurrent:concurrent',
'//src/com/facebook/buck/util/environment:environment',
'//src/com/facebook/buck/zip:steps',
'//src/com/facebook/buck/zip:stream',
diff --git a/src/com/facebook/buck/android/SmartDexingStep.java b/src/com/facebook/buck/android/SmartDexingStep.java
index f329a2f..82187c9 100644
--- a/src/com/facebook/buck/android/SmartDexingStep.java
+++ b/src/com/facebook/buck/android/SmartDexingStep.java
@@ -48,7 +48,6 @@
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
import java.io.File;
import java.io.IOException;
@@ -56,10 +55,12 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Executors;
import javax.annotation.Nullable;
+import static com.facebook.buck.util.concurrent.MoreExecutors.newMultiThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
+
/**
* Optimized dx command runner which can invoke multiple dx commands in parallel and also avoid
* doing unnecessary dx invocations in the first place.
@@ -124,7 +125,7 @@
} else {
numThreadsValue = determineOptimalThreadCount();
}
- return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(numThreadsValue));
+ return listeningDecorator(newMultiThreadExecutor(getClass().getSimpleName(), numThreadsValue));
}
private final ListeningExecutorService getDxExecutor() {
diff --git a/src/com/facebook/buck/cli/AdbCommandRunner.java b/src/com/facebook/buck/cli/AdbCommandRunner.java
index c532198..fdc03b4 100644
--- a/src/com/facebook/buck/cli/AdbCommandRunner.java
+++ b/src/com/facebook/buck/cli/AdbCommandRunner.java
@@ -26,15 +26,16 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
import javax.annotation.Nullable;
+import static com.facebook.buck.util.concurrent.MoreExecutors.newMultiThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
+
/**
* Base class for commands that use the AndroidDebugBridge to run commands on devices.
* Currently, {@link InstallCommand}, {@link UninstallCommand}.
@@ -220,9 +221,8 @@
// Start executions on all matching devices.
List<ListenableFuture<Boolean>> futures = Lists.newArrayList();
- ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(adbThreadCount));
-
+ ListeningExecutorService executorService =
+ listeningDecorator(newMultiThreadExecutor(getClass().getSimpleName(), adbThreadCount));
for (final IDevice device : devices) {
futures.add(executorService.submit(adbCallable.forDevice(device)));
}
diff --git a/src/com/facebook/buck/cli/BUCK b/src/com/facebook/buck/cli/BUCK
index 74081e7..1addbfa 100644
--- a/src/com/facebook/buck/cli/BUCK
+++ b/src/com/facebook/buck/cli/BUCK
@@ -58,6 +58,7 @@
'//src/com/facebook/buck/util:network',
'//src/com/facebook/buck/util:util',
'//src/com/facebook/buck/util/unit:unit',
+ '//src/com/facebook/buck/util/concurrent:concurrent',
'//src/com/facebook/buck/util/environment:environment',
'//src/com/facebook/buck/timing:timing',
'//third-party/java/astyanax:astyanax-cassandra',
diff --git a/src/com/facebook/buck/cli/BuildCommandOptions.java b/src/com/facebook/buck/cli/BuildCommandOptions.java
index ee9b29d..7875a0e 100644
--- a/src/com/facebook/buck/cli/BuildCommandOptions.java
+++ b/src/com/facebook/buck/cli/BuildCommandOptions.java
@@ -34,14 +34,14 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+
+import static com.facebook.buck.util.concurrent.MoreExecutors.newMultiThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
public class BuildCommandOptions extends AbstractCommandOptions {
@@ -130,8 +130,7 @@
}
public ListeningExecutorService createListeningExecutorService() {
- ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
- return MoreExecutors.listeningDecorator(executorService);
+ return listeningDecorator(newMultiThreadExecutor(getClass().getSimpleName(), numThreads));
}
Build createBuild(BuckConfig buckConfig,
diff --git a/src/com/facebook/buck/cli/Main.java b/src/com/facebook/buck/cli/Main.java
index 970fca4..f39b8ad 100644
--- a/src/com/facebook/buck/cli/Main.java
+++ b/src/com/facebook/buck/cli/Main.java
@@ -140,6 +140,7 @@
fileEventBus.register(parser);
fileEventBus.register(hashCache);
webServer = createWebServer(config, console);
+ JavaUtilsLoggingBuildListener.ensureLogFileIsWritten();
}
private Optional<WebServer> createWebServer(BuckConfig config, Console console) {
@@ -346,12 +347,15 @@
// Create or get and invalidate cached command parameters.
Parser parser;
if (context.isPresent()) {
+ // Wire up daemon to new client and console and get cached Parser.
Daemon daemon = getDaemon(projectFilesystem, config, console);
daemon.watchClient(context.get());
daemon.watchFileSystem(console);
- daemon.initWebServer();
+ daemon.initWebServer(); // TODO(user): avoid webserver initialization on each command?
parser = daemon.getParser();
} else {
+ // Initialize logging and create new Parser for new process.
+ JavaUtilsLoggingBuildListener.ensureLogFileIsWritten();
parser = new Parser(projectFilesystem,
knownBuildRuleTypes,
console,
@@ -529,8 +533,6 @@
buckEvents.register(eventListener);
}
- JavaUtilsLoggingBuildListener.ensureLogFileIsWritten();
-
return eventListeners;
}
diff --git a/src/com/facebook/buck/cli/QueryCommand.java b/src/com/facebook/buck/cli/QueryCommand.java
index 3e9d283..04a7aa6 100644
--- a/src/com/facebook/buck/cli/QueryCommand.java
+++ b/src/com/facebook/buck/cli/QueryCommand.java
@@ -24,6 +24,7 @@
import com.facebook.buck.rules.DependencyGraph;
import com.facebook.buck.util.BuckConstant;
import com.facebook.buck.util.HumanReadableException;
+import com.facebook.buck.util.Threads;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
@@ -272,12 +273,14 @@
* @param graphDb database service to shut down.
*/
private static void registerShutdownHook(final GraphDatabaseService graphDb) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- graphDb.shutdown();
- }
- });
+ Runtime.getRuntime().addShutdownHook(Threads.namedThread(
+ QueryCommand.class.getSimpleName(),
+ new Runnable() {
+ @Override
+ public void run() {
+ graphDb.shutdown();
+ }
+ }));
}
private void clearDbPath() {
diff --git a/src/com/facebook/buck/event/BuckEventBus.java b/src/com/facebook/buck/event/BuckEventBus.java
index 4727dbc..35c810b 100644
--- a/src/com/facebook/buck/event/BuckEventBus.java
+++ b/src/com/facebook/buck/event/BuckEventBus.java
@@ -44,7 +44,7 @@
public BuckEventBus(Clock clock, String buildId) {
- this(clock, MoreExecutors.newSingleThreadExecutor(), buildId);
+ this(clock, MoreExecutors.newSingleThreadExecutor(BuckEventBus.class.getSimpleName()), buildId);
}
@VisibleForTesting
diff --git a/src/com/facebook/buck/event/listener/SuperConsoleEventBusListener.java b/src/com/facebook/buck/event/listener/SuperConsoleEventBusListener.java
index 350607f..615fca7 100644
--- a/src/com/facebook/buck/event/listener/SuperConsoleEventBusListener.java
+++ b/src/com/facebook/buck/event/listener/SuperConsoleEventBusListener.java
@@ -32,6 +32,7 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.Comparator;
import java.util.Map;
@@ -77,7 +78,8 @@
this.logEvents = new ConcurrentLinkedQueue<>();
- this.renderScheduler = Executors.newScheduledThreadPool(1);
+ this.renderScheduler = Executors.newScheduledThreadPool(1,
+ new ThreadFactoryBuilder().setNameFormat(getClass().getSimpleName() + "-%d").build());
this.testFormatter = new TestResultFormatter(console.getAnsi());
}
diff --git a/src/com/facebook/buck/json/ProjectBuildFileParser.java b/src/com/facebook/buck/json/ProjectBuildFileParser.java
index f87b5bb..c1f40b4 100644
--- a/src/com/facebook/buck/json/ProjectBuildFileParser.java
+++ b/src/com/facebook/buck/json/ProjectBuildFileParser.java
@@ -19,6 +19,7 @@
import com.facebook.buck.util.Ansi;
import com.facebook.buck.util.InputStreamConsumer;
import com.facebook.buck.util.ProjectFilesystem;
+import com.facebook.buck.util.Threads;
import com.facebook.buck.util.environment.Platform;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
@@ -124,9 +125,11 @@
OutputStream stdin = buckPyProcess.getOutputStream();
InputStream stderr = buckPyProcess.getErrorStream();
- Thread stderrConsumer = new Thread(new InputStreamConsumer(stderr,
- System.err,
- new Ansi(Platform.detect())));
+ Thread stderrConsumer = Threads.namedThread(
+ ProjectBuildFileParser.class.getSimpleName(),
+ new InputStreamConsumer(stderr,
+ System.err,
+ new Ansi(Platform.detect())));
stderrConsumer.start();
buckPyStdinWriter = new BufferedWriter(new OutputStreamWriter(stdin));
diff --git a/src/com/facebook/buck/junit/BuckBlockJUnit4ClassRunner.java b/src/com/facebook/buck/junit/BuckBlockJUnit4ClassRunner.java
index 48ec529..72a57b3 100644
--- a/src/com/facebook/buck/junit/BuckBlockJUnit4ClassRunner.java
+++ b/src/com/facebook/buck/junit/BuckBlockJUnit4ClassRunner.java
@@ -56,7 +56,8 @@
// Executors.newSingleThreadExecutor(). The problem with Executors.newSingleThreadExecutor() is
// that it does not let us specify a RejectedExecutionHandler, which we need to ensure that
// garbage is not spewed to the user's console if the build fails.
- private final ExecutorService executor = MoreExecutors.newSingleThreadExecutor();
+ private final ExecutorService executor =
+ MoreExecutors.newSingleThreadExecutor(getClass().getSimpleName());
private final long defaultTestTimeoutMillis;
diff --git a/src/com/facebook/buck/junit/DelegateRunnerWithTimeout.java b/src/com/facebook/buck/junit/DelegateRunnerWithTimeout.java
index 7cbacb2..c159841 100644
--- a/src/com/facebook/buck/junit/DelegateRunnerWithTimeout.java
+++ b/src/com/facebook/buck/junit/DelegateRunnerWithTimeout.java
@@ -39,7 +39,8 @@
* preserve the behavior of the {@code org.robolectric.RobolectricTestRunner}, we use an
* {@link ExecutorService} with a single thread to run all of the tests.
*/
- private static final ExecutorService executor = MoreExecutors.newSingleThreadExecutor();
+ private static final ExecutorService executor =
+ MoreExecutors.newSingleThreadExecutor(DelegateRunnerWithTimeout.class.getSimpleName());
private final Runner delegate;
private final long defaultTestTimeoutMillis;
diff --git a/src/com/facebook/buck/step/DefaultStepRunner.java b/src/com/facebook/buck/step/DefaultStepRunner.java
index a1826bd..a882df9 100644
--- a/src/com/facebook/buck/step/DefaultStepRunner.java
+++ b/src/com/facebook/buck/step/DefaultStepRunner.java
@@ -25,12 +25,13 @@
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
+
+import static com.facebook.buck.util.concurrent.MoreExecutors.newMultiThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
public final class DefaultStepRunner implements StepRunner {
@@ -41,7 +42,8 @@
* This StepRunner will run all steps on the same thread.
*/
public DefaultStepRunner(ExecutionContext context) {
- this(context, MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1)));
+ this(context,
+ listeningDecorator(newMultiThreadExecutor(DefaultStepRunner.class.getSimpleName(), 1)));
}
public DefaultStepRunner(ExecutionContext context,
diff --git a/src/com/facebook/buck/util/BUCK b/src/com/facebook/buck/util/BUCK
index 37b372c..44c5d66 100644
--- a/src/com/facebook/buck/util/BUCK
+++ b/src/com/facebook/buck/util/BUCK
@@ -36,6 +36,7 @@
'MorePaths.java',
'ProcessExecutor.java',
'ProjectFilesystem.java',
+ 'Threads.java',
'Verbosity.java',
]
java_library(
diff --git a/src/com/facebook/buck/util/BlockingHttpEndpoint.java b/src/com/facebook/buck/util/BlockingHttpEndpoint.java
index 6c3c3c7..3243b07 100644
--- a/src/com/facebook/buck/util/BlockingHttpEndpoint.java
+++ b/src/com/facebook/buck/util/BlockingHttpEndpoint.java
@@ -19,6 +19,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -28,6 +29,7 @@
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -42,6 +44,9 @@
private URL url;
private int timeout = DEFAULT_COMMON_TIMEOUT_MS;
private final ListeningExecutorService requestService;
+ private static final ThreadFactory threadFactory =
+ new ThreadFactoryBuilder().setNameFormat(BlockingHttpEndpoint.class.getSimpleName() + "-%d")
+ .build();
public BlockingHttpEndpoint(String url, int maxParallelRequests) throws MalformedURLException {
this.url = new URL(url);
@@ -54,6 +59,7 @@
2L,
TimeUnit.MINUTES,
workQueue,
+ threadFactory,
new ThreadPoolExecutor.CallerRunsPolicy());
requestService = MoreExecutors.listeningDecorator(executor);
}
diff --git a/src/com/facebook/buck/util/ProcessExecutor.java b/src/com/facebook/buck/util/ProcessExecutor.java
index c246a40..2df73eb 100644
--- a/src/com/facebook/buck/util/ProcessExecutor.java
+++ b/src/com/facebook/buck/util/ProcessExecutor.java
@@ -88,9 +88,9 @@
ansi);
// Consume the streams so they do not deadlock.
- Thread stdOutConsumer = new Thread(stdOut);
+ Thread stdOutConsumer = Threads.namedThread("ProcessExecutor (stdOut)", stdOut);
stdOutConsumer.start();
- Thread stdErrConsumer = new Thread(stdErr);
+ Thread stdErrConsumer = Threads.namedThread("ProcessExecutor (stdErr)", stdErr);
stdErrConsumer.start();
// Block until the Process completes.
diff --git a/src/com/facebook/buck/util/Threads.java b/src/com/facebook/buck/util/Threads.java
new file mode 100644
index 0000000..d04ed8c
--- /dev/null
+++ b/src/com/facebook/buck/util/Threads.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013-present Facebook, Inc.
+ *
+ * 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.facebook.buck.util;
+
+public class Threads {
+
+ public static Thread namedThread(String name, Runnable runnable) {
+ Thread newThread = new Thread(runnable);
+ newThread.setName(name);
+ return newThread;
+ }
+}
diff --git a/src/com/facebook/buck/util/concurrent/MoreExecutors.java b/src/com/facebook/buck/util/concurrent/MoreExecutors.java
index ffd7227..88f9bd4 100644
--- a/src/com/facebook/buck/util/concurrent/MoreExecutors.java
+++ b/src/com/facebook/buck/util/concurrent/MoreExecutors.java
@@ -17,9 +17,12 @@
package com.facebook.buck.util.concurrent;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
public class MoreExecutors {
@@ -28,20 +31,52 @@
}
/**
+ * A ThreadFactory which gives each thread a meaningful and distinct name.
+ * ThreadFactoryBuilder is not used to avoid a dependency on Guava in the junit target.
+ */
+ private static class NamedThreadFactory implements ThreadFactory {
+
+ private final AtomicInteger threadCount = new AtomicInteger(0);
+ private final String threadName;
+
+ NamedThreadFactory(String threadName) {
+ this.threadName = threadName;
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread newThread = Executors.defaultThreadFactory().newThread(r);
+ newThread.setName(String.format(threadName + "-%d", threadCount.incrementAndGet()));
+ return newThread;
+ }
+ }
+
+ /**
* Creates a single threaded executor that silently discards rejected tasks. The problem with
* {@link java.util.concurrent.Executors#newSingleThreadExecutor()} is that it does not let us
* specify a RejectedExecutionHandler, which we need to ensure that garbage is not spewed to the
* user's console if the build fails.
*
* @return A single-threaded executor that silently discards rejected tasks.
+ * @param threadName a thread name prefix used to easily identify threads when debugging.
*/
- public static ExecutorService newSingleThreadExecutor() {
+ public static ExecutorService newSingleThreadExecutor(final String threadName) {
return new ThreadPoolExecutor(
/* corePoolSize */ 1,
/* maximumPoolSize */ 1,
/* keepAliveTime */ 0L, TimeUnit.MILLISECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(),
+ /* threadFactory */ new NamedThreadFactory(threadName),
/* handler */ new ThreadPoolExecutor.DiscardPolicy());
}
+ /**
+ * Creates a multi-threaded executor with meaningfully named threads.
+ * @param threadName a thread name prefix used to easily identify threads when debugging.
+ * @param threadCount the number of threads that should be created in the pool.
+ * @return A multi-threaded executor.
+ */
+ public static ExecutorService newMultiThreadExecutor(final String threadName, int threadCount) {
+ return Executors.newFixedThreadPool(threadCount, new NamedThreadFactory(threadName));
+ }
}
diff --git a/test/com/facebook/buck/step/BUCK b/test/com/facebook/buck/step/BUCK
index a922a24..b5c2c4a 100644
--- a/test/com/facebook/buck/step/BUCK
+++ b/test/com/facebook/buck/step/BUCK
@@ -34,6 +34,7 @@
'//src/com/facebook/buck/step:step',
'//src/com/facebook/buck/util:io',
'//src/com/facebook/buck/util:util',
+ '//src/com/facebook/buck/util/concurrent:concurrent',
'//src/com/facebook/buck/util/environment:environment',
'//test/com/facebook/buck/event:testutil',
'//test/com/facebook/buck/testutil:testutil',
diff --git a/test/com/facebook/buck/step/DefaultStepRunnerTest.java b/test/com/facebook/buck/step/DefaultStepRunnerTest.java
index 4d0ca12..1a3d7fe 100644
--- a/test/com/facebook/buck/step/DefaultStepRunnerTest.java
+++ b/test/com/facebook/buck/step/DefaultStepRunnerTest.java
@@ -30,16 +30,15 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.Test;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import static com.facebook.buck.util.concurrent.MoreExecutors.newMultiThreadExecutor;
+import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
+
public class DefaultStepRunnerTest {
@Test
@@ -59,12 +58,8 @@
.setPlatform(Platform.detect())
.build();
- ThreadFactory threadFactory = new ThreadFactoryBuilder()
- .setDaemon(true)
- .setNameFormat(getClass().getSimpleName() + "-%d")
- .build();
- ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(3, threadFactory));
+ ListeningExecutorService executorService =
+ listeningDecorator(newMultiThreadExecutor(getClass().getSimpleName(), 3));
DefaultStepRunner runner = new DefaultStepRunner(context, executorService);
runner.runStep(passingStep);
try {
@@ -104,12 +99,9 @@
.setEventBus(BuckEventBusFactory.newInstance())
.setPlatform(Platform.detect())
.build();
- ThreadFactory threadFactory = new ThreadFactoryBuilder()
- .setDaemon(true)
- .setNameFormat(getClass().getSimpleName() + "-%d")
- .build();
- ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(3, threadFactory));
+
+ ListeningExecutorService executorService =
+ listeningDecorator(newMultiThreadExecutor(getClass().getSimpleName(), 3));
DefaultStepRunner runner = new DefaultStepRunner(context, executorService);
runner.runStepsInParallelAndWait(steps.build());