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