6829503: addShutdownHook fails if called after shutdown has commenced.
authormchung
Mon, 27 Apr 2009 12:08:41 -0700
changeset 1189164ce9ff8b58
parent 1188 057e4afcf978
child 1190 d2114c1adb2d
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
src/share/classes/java/io/Console.java
src/share/classes/java/io/DeleteOnExitHook.java
src/share/classes/java/lang/ApplicationShutdownHooks.java
src/share/classes/java/lang/Shutdown.java
src/share/classes/java/lang/System.java
src/share/classes/sun/misc/JavaLangAccess.java
test/java/lang/Runtime/shutdown/ShutdownHooks.java
test/java/lang/Runtime/shutdown/ShutdownHooks.sh
     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.";