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());