1 /*
2  * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package java.lang;
27
28
29 import jdk.internal.misc.VM;
30
31 /**
32  * Package-private utility class containing data structures and logic
33  * governing the virtual-machine shutdown sequence.
34  *
35  * @author   Mark Reinhold
36  * @since    1.3
37  *
38  * @see java.io.Console
39  * @see ApplicationShutdownHooks
40  * @see java.io.DeleteOnExitHook
41  */

42
43 class Shutdown {
44
45     // The system shutdown hooks are registered with a predefined slot.
46     // The list of shutdown hooks is as follows:
47     // (0) Console restore hook
48     // (1) ApplicationShutdownHooks that invokes all registered application
49     //     shutdown hooks and waits until they finish
50     // (2) DeleteOnExit hook
51     private static final int MAX_SYSTEM_HOOKS = 10;
52     private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
53
54     // the index of the currently running shutdown hook to the hooks array
55     private static int currentRunningHook = -1;
56
57     /* The preceding static fields are protected by this lock */
58     private static class Lock { };
59     private static Object lock = new Lock();
60
61     /* Lock object for the native halt method */
62     private static Object haltLock = new Lock();
63
64     /**
65      * Add a new system shutdown hook.  Checks the shutdown state and
66      * the hook itself, but does not do any security checks.
67      *
68      * The registerShutdownInProgress parameter should be false except
69      * registering the DeleteOnExitHook since the first file may
70      * be added to the delete on exit list by the application shutdown
71      * hooks.
72      *
73      * @params slot  the slot in the shutdown hook array, whose element
74      *               will be invoked in order during shutdown
75      * @params registerShutdownInProgress true to allow the hook
76      *               to be registered even if the shutdown is in progress.
77      * @params hook  the hook to be registered
78      *
79      * @throws IllegalStateException
80      *         if registerShutdownInProgress is false and shutdown is in progress; or
81      *         if registerShutdownInProgress is true and the shutdown process
82      *         already passes the given slot
83      */

84     static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
85         if (slot < 0 || slot >= MAX_SYSTEM_HOOKS) {
86             throw new IllegalArgumentException("Invalid slot: " + slot);
87         }
88         synchronized (lock) {
89             if (hooks[slot] != null)
90                 throw new InternalError("Shutdown hook at slot " + slot + " already registered");
91
92             if (!registerShutdownInProgress) {
93                 if (currentRunningHook >= 0)
94                     throw new IllegalStateException("Shutdown in progress");
95             } else {
96                 if (VM.isShutdown() || slot <= currentRunningHook)
97                     throw new IllegalStateException("Shutdown in progress");
98             }
99
100             hooks[slot] = hook;
101         }
102     }
103
104     /* Run all system shutdown hooks.
105      *
106      * The system shutdown hooks are run in the thread synchronized on
107      * Shutdown.class.  Other threads calling Runtime::exit, Runtime::halt
108      * or JNI DestroyJavaVM will block indefinitely.
109      *
110      * ApplicationShutdownHooks is registered as one single hook that starts
111      * all application shutdown hooks and waits until they finish.
112      */

113     private static void runHooks() {
114         synchronized (lock) {
115             /* Guard against the possibility of a daemon thread invoking exit
116              * after DestroyJavaVM initiates the shutdown sequence
117              */

118             if (VM.isShutdown()) return;
119         }
120
121         for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
122             try {
123                 Runnable hook;
124                 synchronized (lock) {
125                     // acquire the lock to make sure the hook registered during
126                     // shutdown is visible here.
127                     currentRunningHook = i;
128                     hook = hooks[i];
129                 }
130                 if (hook != null) hook.run();
131             } catch (Throwable t) {
132                 if (t instanceof ThreadDeath) {
133                     ThreadDeath td = (ThreadDeath)t;
134                     throw td;
135                 }
136             }
137         }
138
139         // set shutdown state
140         VM.shutdown();
141     }
142
143     /* Notify the VM that it's time to halt. */
144     static native void beforeHalt();
145
146     /* The halt method is synchronized on the halt lock
147      * to avoid corruption of the delete-on-shutdown file list.
148      * It invokes the true native halt method.
149      */

150     static void halt(int status) {
151         synchronized (haltLock) {
152             halt0(status);
153         }
154     }
155
156     static native void halt0(int status);
157
158     /* Invoked by Runtime.exit, which does all the security checks.
159      * Also invoked by handlers for system-provided termination events,
160      * which should pass a nonzero status code.
161      */

162     static void exit(int status) {
163         synchronized (lock) {
164             if (status != 0 && VM.isShutdown()) {
165                 /* Halt immediately on nonzero status */
166                 halt(status);
167             }
168         }
169         synchronized (Shutdown.class) {
170             /* Synchronize on the class object, causing any other thread
171              * that attempts to initiate shutdown to stall indefinitely
172              */

173             beforeHalt();
174             runHooks();
175             halt(status);
176         }
177     }
178
179
180     /* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
181      * thread has finished.  Unlike the exit method, this method does not
182      * actually halt the VM.
183      */

184     static void shutdown() {
185         synchronized (Shutdown.class) {
186             runHooks();
187         }
188     }
189
190 }
191