1 /*
2  * Copyright (c) 2002-2016, the original author or authors.
3  *
4  * This software is distributable under the BSD license. See the terms of the
5  * BSD license in the documentation provided with this software.
6  *
7  * https://opensource.org/licenses/BSD-3-Clause
8  */
9 package jdk.internal.org.jline.utils;
10 
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.Objects;
14 
15 /**
16  * Manages the JLine shutdown-hook thread and tasks to execute on shutdown.
17  *
18  * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
19  * @since 2.7
20  */
21 public final class ShutdownHooks
22 {
23     private static final List<Task> tasks = new ArrayList<>();
24 
25     private static Thread hook;
26 
add(final T task)27     public static synchronized <T extends Task> T add(final T task) {
28         Objects.requireNonNull(task);
29 
30         // Install the hook thread if needed
31         if (hook == null) {
32             hook = addHook(new Thread("JLine Shutdown Hook")
33             {
34                 @Override
35                 public void run() {
36                     runTasks();
37                 }
38             });
39         }
40 
41         // Track the task
42         Log.debug("Adding shutdown-hook task: ", task);
43         tasks.add(task);
44 
45         return task;
46     }
47 
runTasks()48     private static synchronized void runTasks() {
49         Log.debug("Running all shutdown-hook tasks");
50 
51         // Iterate through copy of tasks list
52         for (Task task : tasks.toArray(new Task[tasks.size()])) {
53             Log.debug("Running task: ", task);
54             try {
55                 task.run();
56             }
57             catch (Throwable e) {
58                 Log.warn("Task failed", e);
59             }
60         }
61 
62         tasks.clear();
63     }
64 
addHook(final Thread thread)65     private static Thread addHook(final Thread thread) {
66         Log.debug("Registering shutdown-hook: ", thread);
67         Runtime.getRuntime().addShutdownHook(thread);
68         return thread;
69     }
70 
remove(final Task task)71     public static synchronized void remove(final Task task) {
72         Objects.requireNonNull(task);
73 
74         // ignore if hook never installed
75         if (hook == null) {
76             return;
77         }
78 
79         // Drop the task
80         tasks.remove(task);
81 
82         // If there are no more tasks, then remove the hook thread
83         if (tasks.isEmpty()) {
84             removeHook(hook);
85             hook = null;
86         }
87     }
88 
removeHook(final Thread thread)89     private static void removeHook(final Thread thread) {
90         Log.debug("Removing shutdown-hook: ", thread);
91 
92         try {
93             Runtime.getRuntime().removeShutdownHook(thread);
94         }
95         catch (IllegalStateException e) {
96             // The VM is shutting down, not a big deal; ignore
97         }
98     }
99 
100     /**
101      * Essentially a {@link Runnable} which allows running to throw an exception.
102      */
103     public interface Task
104     {
run()105         void run() throws Exception;
106     }
107 }
108