6829503: addShutdownHook fails if called after shutdown has commenced.
Summary: allow shutdown hook to be added during shutdown and handle properly if it fails to add
Reviewed-by: alanb, dholmes, martin
1.1 --- a/src/share/classes/java/io/Console.java Thu Apr 23 19:44:43 2009 +0100
1.2 +++ b/src/share/classes/java/io/Console.java Mon Apr 27 12:08:41 2009 -0700
1.3 @@ -503,20 +503,25 @@
1.4
1.5 // Set up JavaIOAccess in SharedSecrets
1.6 static {
1.7 -
1.8 - // Add a shutdown hook to restore console's echo state should
1.9 - // it be necessary.
1.10 - sun.misc.SharedSecrets.getJavaLangAccess()
1.11 - .registerShutdownHook(0 /* shutdown hook invocation order */,
1.12 - new Runnable() {
1.13 - public void run() {
1.14 - try {
1.15 - if (echoOff) {
1.16 - echo(true);
1.17 - }
1.18 - } catch (IOException x) { }
1.19 - }
1.20 - });
1.21 + try {
1.22 + // Add a shutdown hook to restore console's echo state should
1.23 + // it be necessary.
1.24 + sun.misc.SharedSecrets.getJavaLangAccess()
1.25 + .registerShutdownHook(0 /* shutdown hook invocation order */,
1.26 + false /* only register if shutdown is not in progress */,
1.27 + new Runnable() {
1.28 + public void run() {
1.29 + try {
1.30 + if (echoOff) {
1.31 + echo(true);
1.32 + }
1.33 + } catch (IOException x) { }
1.34 + }
1.35 + });
1.36 + } catch (IllegalStateException e) {
1.37 + // shutdown is already in progress and console is first used
1.38 + // by a shutdown hook
1.39 + }
1.40
1.41 sun.misc.SharedSecrets.setJavaIOAccess(new sun.misc.JavaIOAccess() {
1.42 public Console console() {
2.1 --- a/src/share/classes/java/io/DeleteOnExitHook.java Thu Apr 23 19:44:43 2009 +0100
2.2 +++ b/src/share/classes/java/io/DeleteOnExitHook.java Mon Apr 27 12:08:41 2009 -0700
2.3 @@ -34,23 +34,31 @@
2.4 */
2.5
2.6 class DeleteOnExitHook {
2.7 + private static LinkedHashSet<String> files = new LinkedHashSet<String>();
2.8 static {
2.9 - sun.misc.SharedSecrets.getJavaLangAccess()
2.10 - .registerShutdownHook(2 /* Shutdown hook invocation order */,
2.11 - new Runnable() {
2.12 - public void run() {
2.13 - runHooks();
2.14 - }
2.15 - });
2.16 + // DeleteOnExitHook must be the last shutdown hook to be invoked.
2.17 + // Application shutdown hooks may add the first file to the
2.18 + // delete on exit list and cause the DeleteOnExitHook to be
2.19 + // registered during shutdown in progress. So set the
2.20 + // registerShutdownInProgress parameter to true.
2.21 + sun.misc.SharedSecrets.getJavaLangAccess()
2.22 + .registerShutdownHook(2 /* Shutdown hook invocation order */,
2.23 + true /* register even if shutdown in progress */,
2.24 + new Runnable() {
2.25 + public void run() {
2.26 + runHooks();
2.27 + }
2.28 + }
2.29 + );
2.30 }
2.31
2.32 - private static LinkedHashSet<String> files = new LinkedHashSet<String>();
2.33 -
2.34 private DeleteOnExitHook() {}
2.35
2.36 static synchronized void add(String file) {
2.37 - if(files == null)
2.38 + if(files == null) {
2.39 + // DeleteOnExitHook is running. Too late to add a file
2.40 throw new IllegalStateException("Shutdown in progress");
2.41 + }
2.42
2.43 files.add(file);
2.44 }
3.1 --- a/src/share/classes/java/lang/ApplicationShutdownHooks.java Thu Apr 23 19:44:43 2009 +0100
3.2 +++ b/src/share/classes/java/lang/ApplicationShutdownHooks.java Mon Apr 27 12:08:41 2009 -0700
3.3 @@ -35,17 +35,26 @@
3.4 */
3.5
3.6 class ApplicationShutdownHooks {
3.7 + /* The set of registered hooks */
3.8 + private static IdentityHashMap<Thread, Thread> hooks;
3.9 static {
3.10 - Shutdown.add(1 /* shutdown hook invocation order */,
3.11 - new Runnable() {
3.12 - public void run() {
3.13 - runHooks();
3.14 + try {
3.15 + Shutdown.add(1 /* shutdown hook invocation order */,
3.16 + false /* not registered if shutdown in progress */,
3.17 + new Runnable() {
3.18 + public void run() {
3.19 + runHooks();
3.20 + }
3.21 }
3.22 - });
3.23 + );
3.24 + hooks = new IdentityHashMap<Thread, Thread>();
3.25 + } catch (IllegalStateException e) {
3.26 + // application shutdown hooks cannot be added if
3.27 + // shutdown is in progress.
3.28 + hooks = null;
3.29 + }
3.30 }
3.31
3.32 - /* The set of registered hooks */
3.33 - private static IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<Thread, Thread>();
3.34
3.35 private ApplicationShutdownHooks() {}
3.36
4.1 --- a/src/share/classes/java/lang/Shutdown.java Thu Apr 23 19:44:43 2009 +0100
4.2 +++ b/src/share/classes/java/lang/Shutdown.java Mon Apr 27 12:08:41 2009 -0700
4.3 @@ -53,6 +53,9 @@
4.4 private static final int MAX_SYSTEM_HOOKS = 10;
4.5 private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
4.6
4.7 + // the index of the currently running shutdown hook to the hooks array
4.8 + private static int currentRunningHook = 0;
4.9 +
4.10 /* The preceding static fields are protected by this lock */
4.11 private static class Lock { };
4.12 private static Object lock = new Lock();
4.13 @@ -68,17 +71,39 @@
4.14 }
4.15
4.16
4.17 - /* Add a new shutdown hook. Checks the shutdown state and the hook itself,
4.18 + /**
4.19 + * Add a new shutdown hook. Checks the shutdown state and the hook itself,
4.20 * but does not do any security checks.
4.21 + *
4.22 + * The registerShutdownInProgress parameter should be false except
4.23 + * registering the DeleteOnExitHook since the first file may
4.24 + * be added to the delete on exit list by the application shutdown
4.25 + * hooks.
4.26 + *
4.27 + * @params slot the slot in the shutdown hook array, whose element
4.28 + * will be invoked in order during shutdown
4.29 + * @params registerShutdownInProgress true to allow the hook
4.30 + * to be registered even if the shutdown is in progress.
4.31 + * @params hook the hook to be registered
4.32 + *
4.33 + * @throw IllegalStateException
4.34 + * if registerShutdownInProgress is false and shutdown is in progress; or
4.35 + * if registerShutdownInProgress is true and the shutdown process
4.36 + * already passes the given slot
4.37 */
4.38 - static void add(int slot, Runnable hook) {
4.39 + static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
4.40 synchronized (lock) {
4.41 - if (state > RUNNING)
4.42 - throw new IllegalStateException("Shutdown in progress");
4.43 -
4.44 if (hooks[slot] != null)
4.45 throw new InternalError("Shutdown hook at slot " + slot + " already registered");
4.46
4.47 + if (!registerShutdownInProgress) {
4.48 + if (state > RUNNING)
4.49 + throw new IllegalStateException("Shutdown in progress");
4.50 + } else {
4.51 + if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
4.52 + throw new IllegalStateException("Shutdown in progress");
4.53 + }
4.54 +
4.55 hooks[slot] = hook;
4.56 }
4.57 }
4.58 @@ -86,11 +111,15 @@
4.59 /* Run all registered shutdown hooks
4.60 */
4.61 private static void runHooks() {
4.62 - /* We needn't bother acquiring the lock just to read the hooks field,
4.63 - * since the hooks can't be modified once shutdown is in progress
4.64 - */
4.65 - for (Runnable hook : hooks) {
4.66 + for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
4.67 try {
4.68 + Runnable hook;
4.69 + synchronized (lock) {
4.70 + // acquire the lock to make sure the hook registered during
4.71 + // shutdown is visible here.
4.72 + currentRunningHook = i;
4.73 + hook = hooks[i];
4.74 + }
4.75 if (hook != null) hook.run();
4.76 } catch(Throwable t) {
4.77 if (t instanceof ThreadDeath) {
5.1 --- a/src/share/classes/java/lang/System.java Thu Apr 23 19:44:43 2009 +0100
5.2 +++ b/src/share/classes/java/lang/System.java Mon Apr 27 12:08:41 2009 -0700
5.3 @@ -1171,8 +1171,8 @@
5.4 public void blockedOn(Thread t, Interruptible b) {
5.5 t.blockedOn(b);
5.6 }
5.7 - public void registerShutdownHook(int slot, Runnable r) {
5.8 - Shutdown.add(slot, r);
5.9 + public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
5.10 + Shutdown.add(slot, registerShutdownInProgress, hook);
5.11 }
5.12 });
5.13 }
6.1 --- a/src/share/classes/sun/misc/JavaLangAccess.java Thu Apr 23 19:44:43 2009 +0100
6.2 +++ b/src/share/classes/sun/misc/JavaLangAccess.java Mon Apr 27 12:08:41 2009 -0700
6.3 @@ -55,6 +55,22 @@
6.4 /** Set thread's blocker field. */
6.5 void blockedOn(Thread t, Interruptible b);
6.6
6.7 - /** register shutdown hook */
6.8 - void registerShutdownHook(int slot, Runnable r);
6.9 + /**
6.10 + * Registers a shutdown hook.
6.11 + *
6.12 + * It is expected that this method with registerShutdownInProgress=true
6.13 + * is only used to register DeleteOnExitHook since the first file
6.14 + * may be added to the delete on exit list by the application shutdown
6.15 + * hooks.
6.16 + *
6.17 + * @params slot the slot in the shutdown hook array, whose element
6.18 + * will be invoked in order during shutdown
6.19 + * @params registerShutdownInProgress true to allow the hook
6.20 + * to be registered even if the shutdown is in progress.
6.21 + * @params hook the hook to be registered
6.22 + *
6.23 + * @throw IllegalStateException if shutdown is in progress and
6.24 + * the slot is not valid to register.
6.25 + */
6.26 + void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
6.27 }
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/test/java/lang/Runtime/shutdown/ShutdownHooks.java Mon Apr 27 12:08:41 2009 -0700
7.3 @@ -0,0 +1,69 @@
7.4 +/*
7.5 + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7.7 + *
7.8 + * This code is free software; you can redistribute it and/or modify it
7.9 + * under the terms of the GNU General Public License version 2 only, as
7.10 + * published by the Free Software Foundation.
7.11 + *
7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
7.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
7.15 + * version 2 for more details (a copy is included in the LICENSE file that
7.16 + * accompanied this code).
7.17 + *
7.18 + * You should have received a copy of the GNU General Public License version
7.19 + * 2 along with this work; if not, write to the Free Software Foundation,
7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
7.21 + *
7.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
7.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
7.24 + * have any questions.
7.25 + */
7.26 +
7.27 +/*
7.28 + * @bug 6829503
7.29 + * @summary 1) Test Console and DeleteOnExitHook can be initialized
7.30 + * while shutdown is in progress
7.31 + * 2) Test if files that are added by the application shutdown
7.32 + * hook are deleted on exit during shutdown
7.33 + */
7.34 +import java.io.*;
7.35 +public class ShutdownHooks {
7.36 + private static File file;
7.37 + public static void main(String[] args) throws Exception {
7.38 + if (args.length != 2) {
7.39 + throw new IllegalArgumentException("Usage: ShutdownHooks <dir> <filename>");
7.40 + }
7.41 +
7.42 + // Add a shutdown hook
7.43 + Runtime.getRuntime().addShutdownHook(new Cleaner());
7.44 +
7.45 + File dir = new File(args[0]);
7.46 + file = new File(dir, args[1]);
7.47 + // write to file
7.48 + System.out.println("writing to "+ file);
7.49 + PrintWriter pw = new PrintWriter(file);
7.50 + pw.println("Shutdown begins");
7.51 + pw.close();
7.52 + }
7.53 +
7.54 + public static class Cleaner extends Thread {
7.55 + public void run() {
7.56 + // register the Console's shutdown hook while the application
7.57 + // shutdown hook is running
7.58 + Console cons = System.console();
7.59 + // register the DeleteOnExitHook while the application
7.60 + // shutdown hook is running
7.61 + file.deleteOnExit();
7.62 + try {
7.63 + PrintWriter pw = new PrintWriter(file);
7.64 + pw.println("file is being deleted");
7.65 + pw.close();
7.66 + } catch (FileNotFoundException e) {
7.67 + throw new RuntimeException(e);
7.68 + }
7.69 + }
7.70 + }
7.71 +
7.72 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/test/java/lang/Runtime/shutdown/ShutdownHooks.sh Mon Apr 27 12:08:41 2009 -0700
8.3 @@ -0,0 +1,57 @@
8.4 +#!/bin/sh
8.5 +
8.6 +#
8.7 +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
8.8 +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8.9 +#
8.10 +# This code is free software; you can redistribute it and/or modify it
8.11 +# under the terms of the GNU General Public License version 2 only, as
8.12 +# published by the Free Software Foundation.
8.13 +#
8.14 +# This code is distributed in the hope that it will be useful, but WITHOUT
8.15 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8.16 +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
8.17 +# version 2 for more details (a copy is included in the LICENSE file that
8.18 +# accompanied this code).
8.19 +#
8.20 +# You should have received a copy of the GNU General Public License version
8.21 +# 2 along with this work; if not, write to the Free Software Foundation,
8.22 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
8.23 +#
8.24 +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
8.25 +# CA 95054 USA or visit www.sun.com if you need additional information or
8.26 +# have any questions.
8.27 +#
8.28 +
8.29 +
8.30 +# @test
8.31 +# @bug 6829503
8.32 +# @summary 1) Test Console and DeleteOnExitHook can be initialized
8.33 +# while shutdown is in progress
8.34 +# 2) Test if files that are added by the application shutdown
8.35 +# hook are deleted on exit during shutdown
8.36 +#
8.37 +# @build ShutdownHooks
8.38 +# @run shell ShutdownHooks.sh
8.39 +
8.40 +if [ "${TESTJAVA}" = "" ]
8.41 +then
8.42 + echo "TESTJAVA not set. Test cannot execute. Failed."
8.43 + exit 1
8.44 +fi
8.45 +
8.46 +FILENAME=fileToBeDeleted
8.47 +rm -f ${TESTCLASSES}/${FILENAME}
8.48 +
8.49 +# create the file to be deleted on exit
8.50 +echo "testing shutdown" > ${TESTCLASSES}/${FILENAME}
8.51 +
8.52 +${TESTJAVA}/bin/java ${TESTVMOPTS} -classpath ${TESTCLASSES} ShutdownHooks ${TESTCLASSES} $FILENAME
8.53 +if [ $? != 0 ] ; then
8.54 + echo "Test Failed"; exit 1
8.55 +fi
8.56 +
8.57 +if [ -f ${TESTCLASSES}/${FILENAME} ]; then
8.58 + echo "Test Failed: ${TESTCLASSES}/${FILENAME} not deleted"; exit 2
8.59 +fi
8.60 +echo "ShutdownHooks test passed.";