001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.util;
020
021import com.google.common.annotations.VisibleForTesting;
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import java.util.concurrent.ExecutorService;
026import java.util.concurrent.TimeUnit;
027
028/**
029 * Helper class to shutdown {@link Thread}s and {@link ExecutorService}s.
030 */
031public class ShutdownThreadsHelper {
032  private static Log LOG = LogFactory.getLog(ShutdownThreadsHelper.class);
033
034  @VisibleForTesting
035  static final int SHUTDOWN_WAIT_MS = 3000;
036
037  /**
038   * @param thread {@link Thread to be shutdown}
039   * @return <tt>true</tt> if the thread is successfully interrupted,
040   * <tt>false</tt> otherwise
041   * @throws InterruptedException
042   */
043  public static boolean shutdownThread(Thread thread) {
044    return shutdownThread(thread, SHUTDOWN_WAIT_MS);
045  }
046
047  /**
048   * @param thread {@link Thread to be shutdown}
049   * @param timeoutInMilliSeconds time to wait for thread to join after being
050   *                              interrupted
051   * @return <tt>true</tt> if the thread is successfully interrupted,
052   * <tt>false</tt> otherwise
053   * @throws InterruptedException
054   */
055  public static boolean shutdownThread(Thread thread,
056                                    long timeoutInMilliSeconds) {
057    if (thread == null) {
058      return true;
059    }
060
061    try {
062      thread.interrupt();
063      thread.join(timeoutInMilliSeconds);
064      return true;
065    } catch (InterruptedException ie) {
066      LOG.warn("Interrupted while shutting down thread - " + thread.getName());
067      return false;
068    }
069  }
070
071  /**
072   * @param service {@link ExecutorService to be shutdown}
073   * @return <tt>true</tt> if the service is terminated,
074   * <tt>false</tt> otherwise
075   * @throws InterruptedException
076   */
077  public static boolean shutdownExecutorService(ExecutorService service)
078      throws InterruptedException {
079    return shutdownExecutorService(service, SHUTDOWN_WAIT_MS);
080  }
081
082  /**
083   * @param service {@link ExecutorService to be shutdown}
084   * @param timeoutInMs time to wait for {@link
085   * ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit)}
086   *                    calls in milli seconds.
087   * @return <tt>true</tt> if the service is terminated,
088   * <tt>false</tt> otherwise
089   * @throws InterruptedException
090   */
091  public static boolean shutdownExecutorService(ExecutorService service,
092                                        long timeoutInMs)
093      throws InterruptedException {
094    if (service == null) {
095      return true;
096    }
097
098    service.shutdown();
099    if (!service.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS)) {
100      service.shutdownNow();
101      return service.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS);
102    } else {
103      return true;
104    }
105  }
106}