Line data Source code
1 : // Copyright (C) 2009 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.pgm.util; 16 : 17 : import com.google.common.flogger.FluentLogger; 18 : import java.util.ArrayList; 19 : import java.util.List; 20 : 21 : public class RuntimeShutdown { 22 15 : private static final ShutdownCallback cb = new ShutdownCallback(); 23 : 24 : /** Add a task to be performed when graceful shutdown is requested. */ 25 : public static void add(Runnable task) { 26 15 : if (!cb.add(task)) { 27 : // If the shutdown has already begun we cannot enqueue a new 28 : // task. Instead trigger the task in the caller, without any 29 : // of our locks held. 30 : // 31 0 : task.run(); 32 : } 33 15 : } 34 : 35 : /** Wait for the JVM shutdown to occur. */ 36 : public static void waitFor() { 37 15 : cb.waitForShutdown(); 38 15 : } 39 : 40 : public static void manualShutdown() { 41 0 : cb.manualShutdown(); 42 0 : } 43 : 44 : private RuntimeShutdown() {} 45 : 46 : private static class ShutdownCallback extends Thread { 47 15 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 48 : 49 15 : private final List<Runnable> tasks = new ArrayList<>(); 50 : private boolean shutdownStarted; 51 : private boolean shutdownComplete; 52 : 53 15 : ShutdownCallback() { 54 15 : setName("ShutdownCallback"); 55 15 : } 56 : 57 : boolean add(Runnable newTask) { 58 15 : synchronized (this) { 59 15 : if (!shutdownStarted && !shutdownComplete) { 60 15 : if (tasks.isEmpty()) { 61 15 : Runtime.getRuntime().addShutdownHook(this); 62 : } 63 15 : tasks.add(newTask); 64 15 : return true; 65 : } 66 : // We don't permit adding a task once shutdown has started. 67 : // 68 0 : return false; 69 : } 70 : } 71 : 72 : @Override 73 : public void run() { 74 14 : logger.atFine().log("Graceful shutdown requested"); 75 : 76 : List<Runnable> taskList; 77 14 : synchronized (this) { 78 14 : shutdownStarted = true; 79 14 : taskList = tasks; 80 14 : } 81 : 82 14 : for (Runnable task : taskList) { 83 : try { 84 13 : task.run(); 85 0 : } catch (Exception err) { 86 0 : logger.atSevere().withCause(err).log("Cleanup task failed"); 87 13 : } 88 13 : } 89 : 90 13 : logger.atFine().log("Shutdown complete"); 91 : 92 13 : synchronized (this) { 93 13 : shutdownComplete = true; 94 13 : notifyAll(); 95 13 : } 96 13 : } 97 : 98 : void manualShutdown() { 99 0 : Runtime.getRuntime().removeShutdownHook(this); 100 0 : run(); 101 0 : } 102 : 103 : void waitForShutdown() { 104 15 : synchronized (this) { 105 15 : while (!shutdownComplete) { 106 : try { 107 0 : wait(); 108 15 : } catch (InterruptedException e) { 109 15 : return; 110 0 : } 111 : } 112 0 : } 113 0 : } 114 : } 115 : }