001: /*
002: * @(#)Shutdown.java 1.17 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.lang;
029:
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import sun.misc.ThreadRegistry;
033:
034: /**
035: * Package-private utility class containing data structures and logic
036: * governing the virtual-machine shutdown sequence.
037: *
038: * @author Mark Reinhold
039: * @version 1.8, 00/05/03
040: * @since 1.3
041: */
042:
043: class Shutdown {
044:
045: /* Wrapper class for registered hooks, to ensure that hook identity is
046: * object identity rather than .equals identity
047: */
048: private static class WrappedHook {
049:
050: private Thread hook;
051:
052: WrappedHook(Thread t) {
053: hook = t;
054: }
055:
056: public int hashCode() {
057: return System.identityHashCode(hook);
058: }
059:
060: public boolean equals(Object o) {
061: if (!(o instanceof WrappedHook))
062: return false;
063: return (((WrappedHook) o).hook == hook);
064: }
065:
066: }
067:
068: /* Shutdown state */
069: private static final int RUNNING = 0;
070: private static final int HOOKS = 1;
071: private static final int FINALIZERS = 2;
072: private static int state = RUNNING;
073:
074: /* Should we run all finalizers upon exit? */
075: private static boolean runFinalizersOnExit = false;
076:
077: /* The set of registered, wrapped hooks, or null if there aren't any */
078: private static HashSet hooks = null;
079:
080: /* The preceding static fields are protected by this lock */
081: private static class Lock {
082: };
083:
084: private static Object lock = new Lock();
085:
086: /* Invoked by Runtime.runFinalizersOnExit */
087: static void setRunFinalizersOnExit(boolean run) {
088: synchronized (lock) {
089: runFinalizersOnExit = run;
090: }
091: }
092:
093: /* Add a new shutdown hook. Checks the shutdown state and the hook itself,
094: * but does not do any security checks.
095: */
096: static void add(Thread hook) {
097: synchronized (lock) {
098: if (state > RUNNING)
099: throw new IllegalStateException("Shutdown in progress");
100: if (hook.isAlive())
101: throw new IllegalArgumentException(
102: "Hook already running");
103: if (hooks == null) {
104: hooks = new HashSet(11);
105: hooks.add(new WrappedHook(hook));
106: Terminator.setup();
107: } else {
108: WrappedHook wh = new WrappedHook(hook);
109: if (hooks.contains(wh))
110: throw new IllegalArgumentException(
111: "Hook previously registered");
112: hooks.add(wh);
113: }
114: }
115: }
116:
117: /* Remove a previously-registered hook. Like the add method, this method
118: * does not do any security checks.
119: */
120: static boolean remove(Thread hook) {
121: synchronized (lock) {
122: if (state > RUNNING)
123: throw new IllegalStateException("Shutdown in progress");
124: if (hook == null)
125: throw new NullPointerException();
126: if (hooks == null) {
127: return false;
128: } else {
129: boolean rv = hooks.remove(new WrappedHook(hook));
130: if (rv && hooks.isEmpty()) {
131: hooks = null;
132: Terminator.teardown();
133: }
134: return rv;
135: }
136: }
137: }
138:
139: /* Run all registered shutdown hooks
140: */
141: private static void runHooks() {
142: /* We needn't bother acquiring the lock just to read the hooks field,
143: * since the hooks can't be modified once shutdown is in progress
144: */
145: if (hooks == null)
146: return;
147: for (Iterator i = hooks.iterator(); i.hasNext();) {
148: ((WrappedHook) (i.next())).hook.start();
149: }
150: for (Iterator i = hooks.iterator(); i.hasNext();) {
151: try {
152: ((WrappedHook) (i.next())).hook.join();
153: } catch (InterruptedException x) {
154: continue;
155: }
156: }
157: }
158:
159: /* The true native halt method; also invoked by Runtime.halt
160: * after doing the necessary security checks
161: */
162: static native void halt(int status);
163:
164: /* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
165: private static native void runAllFinalizers();
166:
167: /* The actual shutdown sequence is defined here.
168: *
169: * If it weren't for runFinalizersOnExit, this would be simple -- we'd just
170: * run the hooks and then halt. Instead we need to keep track of whether
171: * we're running hooks or finalizers. In the latter case a finalizer could
172: * invoke exit(1) to cause immediate termination, while in the former case
173: * any further invocations of exit(n), for any n, simply stall. Note that
174: * if on-exit finalizers are enabled they're run iff the shutdown is
175: * initiated by an exit(0); they're never run on exit(n) for n != 0 or in
176: * response to SIGINT, SIGTERM, etc.
177: */
178: private static void sequence() {
179: synchronized (lock) {
180: /* Guard against the possibility of a daemon thread invoking exit
181: * after DestroyJavaVM initiates the shutdown sequence
182: */
183: if (state != HOOKS)
184: return;
185: }
186: runHooks();
187: boolean rfoe;
188: synchronized (lock) {
189: state = FINALIZERS;
190: rfoe = runFinalizersOnExit;
191: }
192: if (rfoe)
193: runAllFinalizers();
194: }
195:
196: /* Invoked by Runtime.exit, which does all the security checks.
197: * Also invoked by handlers for system-provided termination events,
198: * which should pass a nonzero status code.
199: */
200: static void exit(int status) {
201: boolean runMoreFinalizers = false;
202: synchronized (lock) {
203: if (status != 0)
204: runFinalizersOnExit = false;
205: switch (state) {
206: case RUNNING: /* Initiate shutdown */
207: state = HOOKS;
208: break;
209: case HOOKS: /* Stall and halt */
210: break;
211: case FINALIZERS:
212: if (status != 0) {
213: /* Halt immediately on nonzero status */
214: halt(status);
215: } else {
216: /* Compatibility with old behavior:
217: * Run more finalizers and then halt
218: */
219: runMoreFinalizers = runFinalizersOnExit;
220: }
221: break;
222: }
223: }
224: if (runMoreFinalizers) {
225: runAllFinalizers();
226: halt(status);
227: }
228: synchronized (Shutdown.class) {
229: /* Synchronize on the class object, causing any other thread
230: * that attempts to initiate shutdown to stall indefinitely
231: */
232: sequence();
233: halt(status);
234: }
235: }
236:
237: /* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
238: * thread has finished. Unlike the exit method, this method does not
239: * actually halt the VM.
240: */
241: static void shutdown() {
242: synchronized (lock) {
243: switch (state) {
244: case RUNNING: /* Initiate shutdown */
245: state = HOOKS;
246: break;
247: case HOOKS: /* Stall and then return */
248: case FINALIZERS:
249: break;
250: }
251: }
252: synchronized (Shutdown.class) {
253: sequence();
254: }
255: }
256:
257: private static void waitAllUserThreadsExitAndShutdown() {
258: ThreadRegistry.waitAllUserThreadsExit();
259: shutdown();
260: }
261: }
|