FileDocCategorySizeDatePackage
Shutdown.javaAPI DocJava SE 6 API5533Tue Jun 10 00:25:36 BST 2008java.lang

Shutdown.java

/*
 * @(#)Shutdown.java	1.13 05/12/01
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang;

import java.util.ArrayList;


/**
 * Package-private utility class containing data structures and logic
 * governing the virtual-machine shutdown sequence.
 *
 * @author   Mark Reinhold
 * @version  1.13, 05/12/01
 * @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 set of registered, wrapped hooks, or null if there aren't any */
    private static ArrayList<Runnable> hooks = new ArrayList<Runnable>();

    /* 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.
     */
    static void add(Runnable hook) {
	synchronized (lock) {
	    if (state > RUNNING)
		throw new IllegalStateException("Shutdown in progress");

	    hooks.add(hook);
	}
    }


    /* Remove a previously-registered hook.  Like the add method, this method
     * does not do any security checks.
     */
    static boolean remove(Runnable hook) {
	synchronized (lock) {
	    if (state > RUNNING)
		throw new IllegalStateException("Shutdown in progress");
	    if (hook == null) throw new NullPointerException();
	    if (hooks == null) {
		return false;
	    } else {
		return hooks.remove(hook);
	    }
	}
    }


    /* Run all registered shutdown hooks
     */
    private static void runHooks() {
	/* We needn't bother acquiring the lock just to read the hooks field,
	 * since the hooks can't be modified once shutdown is in progress
	 */
	for (Runnable hook : hooks) {
	    try {
		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();
	}
    }

}