| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.util; |
| |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Utility methods for working with thread pools {@link ExecutorService}. |
| */ |
| public final class ThreadPoolUtils { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolUtils.class); |
| |
| /** |
| * Default thread pool shutdown timeout used to await graceful shutdown. |
| */ |
| public static final long DEFAULT_SHUTDOWN_AWAIT_TERMINATION = 10 * 1000L; |
| |
| /** |
| * Shutdown the given executor service only (ie not graceful shutdown). |
| * |
| * @param executorService |
| * The ExecutorService that is being shutdown. |
| * |
| * @see java.util.concurrent.ExecutorService#shutdown() |
| */ |
| public static void shutdown(ExecutorService executorService) { |
| doShutdown(executorService, 0); |
| } |
| |
| /** |
| * Shutdown now the given executor service aggressively. |
| * |
| * @param executorService |
| * the executor service to shutdown now |
| * |
| * @return list of tasks that never commenced execution |
| * |
| * @see java.util.concurrent.ExecutorService#shutdownNow() |
| */ |
| public static List<Runnable> shutdownNow(ExecutorService executorService) { |
| if (executorService == null) { |
| return Collections.emptyList(); |
| } |
| |
| List<Runnable> answer = null; |
| if (!executorService.isShutdown()) { |
| LOG.debug("Forcing shutdown of ExecutorService: {}", executorService); |
| answer = executorService.shutdownNow(); |
| LOG.trace("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {}.", |
| new Object[] { executorService, executorService.isShutdown(), executorService.isTerminated() }); |
| } |
| |
| if (answer == null) { |
| answer = Collections.emptyList(); |
| } |
| |
| return answer; |
| } |
| |
| /** |
| * Shutdown the given executor service graceful at first, and then aggressively if the await |
| * termination timeout was hit. |
| * <p> |
| * This implementation invokes the |
| * {@link #shutdownGraceful(java.util.concurrent.ExecutorService, long)} with a timeout |
| * value of {@link #DEFAULT_SHUTDOWN_AWAIT_TERMINATION} millis. |
| * |
| * @param executorService |
| * The ExecutorService that is being shutdown. |
| */ |
| public static void shutdownGraceful(ExecutorService executorService) { |
| doShutdown(executorService, DEFAULT_SHUTDOWN_AWAIT_TERMINATION); |
| } |
| |
| /** |
| * Shutdown the given executor service graceful at first, and then aggressively if the await |
| * termination timeout was hit. |
| * <p> |
| * Will try to perform an orderly shutdown by giving the running threads time to complete |
| * tasks, before going more aggressively by doing a |
| * {@link #shutdownNow(java.util.concurrent.ExecutorService)} which forces a shutdown. The |
| * parameter <code>shutdownAwaitTermination</code> is used as timeout value waiting for orderly |
| * shutdown to complete normally, before going aggressively. |
| * |
| * @param executorService |
| * the executor service to shutdown |
| * @param shutdownAwaitTermination |
| * timeout in millis to wait for orderly shutdown |
| */ |
| public static void shutdownGraceful(ExecutorService executorService, long shutdownAwaitTermination) { |
| doShutdown(executorService, shutdownAwaitTermination); |
| } |
| |
| private static void doShutdown(ExecutorService executorService, long shutdownAwaitTermination) { |
| if (executorService == null) { |
| return; |
| } |
| |
| // shutting down a thread pool is a 2 step process. First we try graceful, and if |
| // that fails, then we go more aggressively and try shutting down again. In both |
| // cases we wait at most the given shutdown timeout value given |
| // |
| // total wait could then be 2 x shutdownAwaitTermination, but when we shutdown the |
| // 2nd time we are aggressive and thus we ought to shutdown much faster |
| if (!executorService.isShutdown()) { |
| boolean warned = false; |
| StopWatch watch = new StopWatch(); |
| |
| LOG.trace("Shutdown of ExecutorService: {} with await termination: {} millis", executorService, shutdownAwaitTermination); |
| executorService.shutdown(); |
| |
| if (shutdownAwaitTermination > 0) { |
| try { |
| if (!awaitTermination(executorService, shutdownAwaitTermination)) { |
| warned = true; |
| LOG.warn("Forcing shutdown of ExecutorService: {} due first await termination elapsed.", executorService); |
| executorService.shutdownNow(); |
| // we are now shutting down aggressively, so wait to see |
| // if we can completely shutdown or not |
| if (!awaitTermination(executorService, shutdownAwaitTermination)) { |
| LOG.warn("Cannot completely force shutdown of ExecutorService: {} due second await termination elapsed.", executorService); |
| } |
| } |
| } catch (InterruptedException e) { |
| warned = true; |
| LOG.warn("Forcing shutdown of ExecutorService: {} due interrupted.", executorService); |
| // we were interrupted during shutdown, so force shutdown |
| executorService.shutdownNow(); |
| } |
| } |
| |
| // if we logged at WARN level, then report at INFO level when we are |
| // complete so the end user can see this in the log |
| if (warned) { |
| LOG.info("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {} took: {}.", |
| executorService, executorService.isShutdown(), executorService.isTerminated(), TimeUtils.printDuration(watch.taken())); |
| } else if (LOG.isDebugEnabled()) { |
| LOG.debug("Shutdown of ExecutorService: {} is shutdown: {} and terminated: {} took: {}.", |
| executorService, executorService.isShutdown(), executorService.isTerminated(), TimeUtils.printDuration(watch.taken())); |
| } |
| } |
| } |
| |
| /** |
| * Awaits the termination of the thread pool. |
| * <p> |
| * This implementation will log every 2nd second at INFO level that we are waiting, so the |
| * end user can see we are not hanging in case it takes longer time to terminate the pool. |
| * |
| * @param executorService |
| * the thread pool |
| * @param shutdownAwaitTermination |
| * time in millis to use as timeout |
| * |
| * @return <code>true</code> if the pool is terminated, or <code>false</code> if we timed out |
| * |
| * @throws InterruptedException |
| * is thrown if we are interrupted during the waiting |
| */ |
| public static boolean awaitTermination(ExecutorService executorService, long shutdownAwaitTermination) throws InterruptedException { |
| |
| if (executorService == null) { |
| return true; |
| } |
| |
| // log progress every 5th second so end user is aware of we are shutting down |
| StopWatch watch = new StopWatch(); |
| long interval = Math.min(2000, shutdownAwaitTermination); |
| boolean done = false; |
| while (!done && interval > 0) { |
| if (executorService.awaitTermination(interval, TimeUnit.MILLISECONDS)) { |
| done = true; |
| } else { |
| LOG.debug("Waited {} for ExecutorService: {} to terminate...", TimeUtils.printDuration(watch.taken()), executorService); |
| // recalculate interval |
| interval = Math.min(2000, shutdownAwaitTermination - watch.taken()); |
| } |
| } |
| |
| return done; |
| } |
| } |