/*
* Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang;
/**
* Package-private utility class containing data structures and logic
* governing the virtual-machine shutdown sequence.
*
* @author Mark Reinhold
* @since 1.3
*/
class
Shutdown {
/* Shutdown state */
private static final int
RUNNING = 0;
private static final int
HOOKS = 1;
private static final int
FINALIZERS = 2;
private static int
state =
RUNNING;
/* Should we run all finalizers upon exit? */
private static boolean
runFinalizersOnExit = false;
// The system shutdown hooks are registered with a predefined slot.
// The list of shutdown hooks is as follows:
// (0) Console restore hook
// (1) Application hooks
// (2) DeleteOnExit hook
private static final int
MAX_SYSTEM_HOOKS = 10;
private static final
Runnable[]
hooks = new
Runnable[
MAX_SYSTEM_HOOKS];
// the index of the currently running shutdown hook to the hooks array
private static int
currentRunningHook = 0;
/* The preceding static fields are protected by this lock */
private static class
Lock { };
private static
Object lock = new
Lock();
/* Lock object for the native halt method */
private static
Object haltLock = new
Lock();
/* Invoked by Runtime.runFinalizersOnExit */
static void
setRunFinalizersOnExit(boolean
run) {
synchronized (
lock) {
runFinalizersOnExit =
run;
}
}
/**
* Add a new shutdown hook. Checks the shutdown state and the hook itself,
* but does not do any security checks.
*
* The registerShutdownInProgress parameter should be false except
* registering the DeleteOnExitHook since the first file may
* be added to the delete on exit list by the application shutdown
* hooks.
*
* @params slot the slot in the shutdown hook array, whose element
* will be invoked in order during shutdown
* @params registerShutdownInProgress true to allow the hook
* to be registered even if the shutdown is in progress.
* @params hook the hook to be registered
*
* @throw IllegalStateException
* if registerShutdownInProgress is false and shutdown is in progress; or
* if registerShutdownInProgress is true and the shutdown process
* already passes the given slot
*/
static void
add(int
slot, boolean
registerShutdownInProgress,
Runnable hook) {
synchronized (
lock) {
if (
hooks[
slot] != null)
throw new
InternalError("Shutdown hook at slot " +
slot + " already registered");
if (!
registerShutdownInProgress) {
if (
state >
RUNNING)
throw new
IllegalStateException("Shutdown in progress");
} else {
if (
state >
HOOKS || (
state ==
HOOKS &&
slot <=
currentRunningHook))
throw new
IllegalStateException("Shutdown in progress");
}
hooks[
slot] =
hook;
}
}
/* Run all registered shutdown hooks
*/
private static void
runHooks() {
for (int
i=0;
i <
MAX_SYSTEM_HOOKS;
i++) {
try {
Runnable hook;
synchronized (
lock) {
// acquire the lock to make sure the hook registered during
// shutdown is visible here.
currentRunningHook =
i;
hook =
hooks[
i];
}
if (
hook != null)
hook.
run();
} catch(
Throwable t) {
if (
t instanceof
ThreadDeath) {
ThreadDeath td = (
ThreadDeath)
t;
throw
td;
}
}
}
}
/* The halt method is synchronized on the halt lock
* to avoid corruption of the delete-on-shutdown file list.
* It invokes the true native halt method.
*/
static void
halt(int
status) {
synchronized (
haltLock) {
halt0(
status);
}
}
static native void
halt0(int
status);
/* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
private static native void
runAllFinalizers();
/* The actual shutdown sequence is defined here.
*
* If it weren't for runFinalizersOnExit, this would be simple -- we'd just
* run the hooks and then halt. Instead we need to keep track of whether
* we're running hooks or finalizers. In the latter case a finalizer could
* invoke exit(1) to cause immediate termination, while in the former case
* any further invocations of exit(n), for any n, simply stall. Note that
* if on-exit finalizers are enabled they're run iff the shutdown is
* initiated by an exit(0); they're never run on exit(n) for n != 0 or in
* response to SIGINT, SIGTERM, etc.
*/
private static void
sequence() {
synchronized (
lock) {
/* Guard against the possibility of a daemon thread invoking exit
* after DestroyJavaVM initiates the shutdown sequence
*/
if (
state !=
HOOKS) return;
}
runHooks();
boolean
rfoe;
synchronized (
lock) {
state =
FINALIZERS;
rfoe =
runFinalizersOnExit;
}
if (
rfoe)
runAllFinalizers();
}
/* Invoked by Runtime.exit, which does all the security checks.
* Also invoked by handlers for system-provided termination events,
* which should pass a nonzero status code.
*/
static void
exit(int
status) {
boolean
runMoreFinalizers = false;
synchronized (
lock) {
if (
status != 0)
runFinalizersOnExit = false;
switch (
state) {
case
RUNNING: /* Initiate shutdown */
state =
HOOKS;
break;
case
HOOKS: /* Stall and halt */
break;
case
FINALIZERS:
if (
status != 0) {
/* Halt immediately on nonzero status */
halt(
status);
} else {
/* Compatibility with old behavior:
* Run more finalizers and then halt
*/
runMoreFinalizers =
runFinalizersOnExit;
}
break;
}
}
if (
runMoreFinalizers) {
runAllFinalizers();
halt(
status);
}
synchronized (
Shutdown.class) {
/* Synchronize on the class object, causing any other thread
* that attempts to initiate shutdown to stall indefinitely
*/
sequence();
halt(
status);
}
}
/* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
* thread has finished. Unlike the exit method, this method does not
* actually halt the VM.
*/
static void
shutdown() {
synchronized (
lock) {
switch (
state) {
case
RUNNING: /* Initiate shutdown */
state =
HOOKS;
break;
case
HOOKS: /* Stall and then return */
case
FINALIZERS:
break;
}
}
synchronized (
Shutdown.class) {
sequence();
}
}
}