1. /*
  2. * @(#)Shutdown.java 1.11 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.lang;
  8. import java.util.HashSet;
  9. import java.util.Iterator;
  10. /**
  11. * Package-private utility class containing data structures and logic
  12. * governing the virtual-machine shutdown sequence.
  13. *
  14. * @author Mark Reinhold
  15. * @version 1.11, 03/12/19
  16. * @since 1.3
  17. */
  18. class Shutdown {
  19. /* Wrapper class for registered hooks, to ensure that hook identity is
  20. * object identity rather than .equals identity
  21. */
  22. private static class WrappedHook {
  23. private Thread hook;
  24. WrappedHook(Thread t) {
  25. hook = t;
  26. }
  27. public int hashCode() {
  28. return System.identityHashCode(hook);
  29. }
  30. public boolean equals(Object o) {
  31. if (!(o instanceof WrappedHook)) return false;
  32. return (((WrappedHook)o).hook == hook);
  33. }
  34. }
  35. /* Shutdown state */
  36. private static final int RUNNING = 0;
  37. private static final int HOOKS = 1;
  38. private static final int FINALIZERS = 2;
  39. private static int state = RUNNING;
  40. /* Should we run all finalizers upon exit? */
  41. private static boolean runFinalizersOnExit = false;
  42. /* The set of registered, wrapped hooks, or null if there aren't any */
  43. private static HashSet hooks = null;
  44. /* The preceding static fields are protected by this lock */
  45. private static class Lock { };
  46. private static Object lock = new Lock();
  47. /* Lock object for the native halt method */
  48. private static Object haltLock = new Lock();
  49. /* Invoked by Runtime.runFinalizersOnExit */
  50. static void setRunFinalizersOnExit(boolean run) {
  51. synchronized (lock) {
  52. runFinalizersOnExit = run;
  53. }
  54. }
  55. /* Add a new shutdown hook. Checks the shutdown state and the hook itself,
  56. * but does not do any security checks.
  57. */
  58. static void add(Thread hook) {
  59. synchronized (lock) {
  60. if (state > RUNNING)
  61. throw new IllegalStateException("Shutdown in progress");
  62. if (hook.isAlive())
  63. throw new IllegalArgumentException("Hook already running");
  64. if (hooks == null) {
  65. hooks = new HashSet(11);
  66. hooks.add(new WrappedHook(hook));
  67. Terminator.setup();
  68. } else {
  69. WrappedHook wh = new WrappedHook(hook);
  70. if (hooks.contains(wh))
  71. throw new IllegalArgumentException("Hook previously registered");
  72. hooks.add(wh);
  73. }
  74. }
  75. }
  76. /* Remove a previously-registered hook. Like the add method, this method
  77. * does not do any security checks.
  78. */
  79. static boolean remove(Thread hook) {
  80. synchronized (lock) {
  81. if (state > RUNNING)
  82. throw new IllegalStateException("Shutdown in progress");
  83. if (hook == null) throw new NullPointerException();
  84. if (hooks == null) {
  85. return false;
  86. } else {
  87. boolean rv = hooks.remove(new WrappedHook(hook));
  88. if (rv && hooks.isEmpty()) {
  89. hooks = null;
  90. Terminator.teardown();
  91. }
  92. return rv;
  93. }
  94. }
  95. }
  96. /* Run all registered shutdown hooks
  97. */
  98. private static void runHooks() {
  99. /* We needn't bother acquiring the lock just to read the hooks field,
  100. * since the hooks can't be modified once shutdown is in progress
  101. */
  102. if (hooks == null) return;
  103. for (Iterator i = hooks.iterator(); i.hasNext();) {
  104. ((WrappedHook)(i.next())).hook.start();
  105. }
  106. for (Iterator i = hooks.iterator(); i.hasNext();) {
  107. try {
  108. ((WrappedHook)(i.next())).hook.join();
  109. } catch (InterruptedException x) {
  110. continue;
  111. }
  112. }
  113. }
  114. /* The halt method is synchronized on the halt lock
  115. * to avoid corruption of the delete-on-shutdown file list.
  116. * It invokes the true native halt method.
  117. */
  118. static void halt(int status) {
  119. synchronized (haltLock) {
  120. halt0(status);
  121. }
  122. }
  123. static native void halt0(int status);
  124. /* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
  125. private static native void runAllFinalizers();
  126. /* The actual shutdown sequence is defined here.
  127. *
  128. * If it weren't for runFinalizersOnExit, this would be simple -- we'd just
  129. * run the hooks and then halt. Instead we need to keep track of whether
  130. * we're running hooks or finalizers. In the latter case a finalizer could
  131. * invoke exit(1) to cause immediate termination, while in the former case
  132. * any further invocations of exit(n), for any n, simply stall. Note that
  133. * if on-exit finalizers are enabled they're run iff the shutdown is
  134. * initiated by an exit(0); they're never run on exit(n) for n != 0 or in
  135. * response to SIGINT, SIGTERM, etc.
  136. */
  137. private static void sequence() {
  138. synchronized (lock) {
  139. /* Guard against the possibility of a daemon thread invoking exit
  140. * after DestroyJavaVM initiates the shutdown sequence
  141. */
  142. if (state != HOOKS) return;
  143. }
  144. runHooks();
  145. boolean rfoe;
  146. synchronized (lock) {
  147. state = FINALIZERS;
  148. rfoe = runFinalizersOnExit;
  149. }
  150. if (rfoe) runAllFinalizers();
  151. }
  152. /* Invoked by Runtime.exit, which does all the security checks.
  153. * Also invoked by handlers for system-provided termination events,
  154. * which should pass a nonzero status code.
  155. */
  156. static void exit(int status) {
  157. boolean runMoreFinalizers = false;
  158. synchronized (lock) {
  159. if (status != 0) runFinalizersOnExit = false;
  160. switch (state) {
  161. case RUNNING: /* Initiate shutdown */
  162. state = HOOKS;
  163. break;
  164. case HOOKS: /* Stall and halt */
  165. break;
  166. case FINALIZERS:
  167. if (status != 0) {
  168. /* Halt immediately on nonzero status */
  169. halt(status);
  170. } else {
  171. /* Compatibility with old behavior:
  172. * Run more finalizers and then halt
  173. */
  174. runMoreFinalizers = runFinalizersOnExit;
  175. }
  176. break;
  177. }
  178. }
  179. if (runMoreFinalizers) {
  180. runAllFinalizers();
  181. halt(status);
  182. }
  183. synchronized (Shutdown.class) {
  184. /* Synchronize on the class object, causing any other thread
  185. * that attempts to initiate shutdown to stall indefinitely
  186. */
  187. sequence();
  188. halt(status);
  189. }
  190. }
  191. /* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
  192. * thread has finished. Unlike the exit method, this method does not
  193. * actually halt the VM.
  194. */
  195. static void shutdown() {
  196. synchronized (lock) {
  197. switch (state) {
  198. case RUNNING: /* Initiate shutdown */
  199. state = HOOKS;
  200. break;
  201. case HOOKS: /* Stall and then return */
  202. case FINALIZERS:
  203. break;
  204. }
  205. }
  206. synchronized (Shutdown.class) {
  207. sequence();
  208. }
  209. }
  210. }