// Copyright 2014 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.lib.concurrent; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.devtools.build.lib.util.Preconditions; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Useful utilities for executors. */ public class ExecutorUtil { private ExecutorUtil() { } /** * Shutdown the executor via shutdownNow(). If an interrupt occurs, ignore it (since the tasks * were already interrupted by the initial shutdownNow) and still block on the eventual * termination of the pool. * * @param executor the executor service. * @return true iff interrupted. */ public static boolean uninterruptibleShutdownNow(ExecutorService executor) { return shutdownImpl(executor, /*shutdownNowInitially=*/true, /*shutdownNowOnInterrupt=*/false); } /** * Shutdown the executor via shutdown(). If an interrupt occurs, invoke shutdownNow(), but * still block on the eventual termination of the pool. * * @param executor the executor service. * @return true iff interrupted. */ public static boolean interruptibleShutdown(ExecutorService executor) { return shutdownImpl( executor, /*shutdownNowInitially=*/false, /*shutdownNowOnInterrupt=*/true); } /** * Shutdown the executor via shutdown(). If an interrupt occurs, ignore it and still block on the * eventual termination of the pool. This way, all tasks are guaranteed to have completed * normally. * * @param executor the executor service. * @return true iff interrupted. */ public static boolean uninterruptibleShutdown(ExecutorService executor) { return shutdownImpl(executor, /*shutdownNowInitially=*/false, /*shutdownNowOnInterrupt=*/false); } private static boolean shutdownImpl( ExecutorService executor, boolean shutdownNowInitially, boolean shutdownNowOnInterrupt) { Preconditions.checkState(!executor.isShutdown()); if (shutdownNowInitially) { executor.shutdownNow(); } else { executor.shutdown(); } // Common pattern: check for interrupt, but don't return until all threads // are finished. boolean interrupted = false; while (true) { try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); break; } catch (InterruptedException e) { if (shutdownNowOnInterrupt) { executor.shutdownNow(); } interrupted = true; } } return interrupted; } /** * Create a "slack" thread pool which has the following properties: * 1. the worker count shrinks as the threads go unused * 2. the rejection policy is caller-runs * * @param threads maximum number of threads in the pool * @param name name of the pool * @return the new ThreadPoolExecutor */ public static ThreadPoolExecutor newSlackPool(int threads, String name) { // Using a synchronous queue with a bounded thread pool means we'll reject // tasks after the pool size. The CallerRunsPolicy, however, implies that // saturation is handled in the calling thread. ThreadPoolExecutor pool = new ThreadPoolExecutor(threads, threads, 3L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); // Do not consume threads when not in use. pool.allowCoreThreadTimeOut(true); pool.setThreadFactory(new ThreadFactoryBuilder().setNameFormat(name + " %d").build()); pool.setRejectedExecutionHandler(new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { r.run(); } }); return pool; } }