// Copyright (C) 2009 The Android Open Source Project // // 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.gerrit.pgm.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public class RuntimeShutdown { private static final ShutdownCallback cb = new ShutdownCallback(); /** Add a task to be performed when graceful shutdown is requested. */ public static void add(final Runnable task) { if (!cb.add(task)) { // If the shutdown has already begun we cannot enqueue a new // task. Instead trigger the task in the caller, without any // of our locks held. // task.run(); } } /** Wait for the JVM shutdown to occur. */ public static void waitFor() { cb.waitForShutdown(); } private RuntimeShutdown() { } private static class ShutdownCallback extends Thread { private static final Logger log = LoggerFactory.getLogger(ShutdownCallback.class); private final List<Runnable> tasks = new ArrayList<>(); private boolean shutdownStarted; private boolean shutdownComplete; ShutdownCallback() { setName("ShutdownCallback"); } boolean add(final Runnable newTask) { synchronized (this) { if (!shutdownStarted && !shutdownComplete) { if (tasks.isEmpty()) { Runtime.getRuntime().addShutdownHook(this); } tasks.add(newTask); return true; } else { // We don't permit adding a task once shutdown has started. // return false; } } } @Override public void run() { log.debug("Graceful shutdown requested"); List<Runnable> taskList; synchronized (this) { shutdownStarted = true; taskList = tasks; } for (Runnable task : taskList) { try { task.run(); } catch (Exception err) { log.error("Cleanup task failed", err); } } log.debug("Shutdown complete"); synchronized (this) { shutdownComplete = true; notifyAll(); } } void waitForShutdown() { synchronized (this) { while (!shutdownComplete) { try { wait(); } catch (InterruptedException e) { return; } } } } } }