JDK-4772381 : System.exit not working with shutdownhook and JWS console
  • Type: Bug
  • Component: deploy
  • Sub-Component: webstart
  • Affected Version: 1.2.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-11-01
  • Updated: 2003-01-14
  • Resolved: 2003-01-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.2 b13Fixed
Description

Name: nt126004			Date: 10/31/2002


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
Service Pack 3 installed
French version of Operating System and Service Pack


A DESCRIPTION OF THE PROBLEM :
System.exit(0) doesn't end the JVM for a Swing application
(not tested with other kinds) when the following conditions
are true together :

- A shutdownhook has been registered
- The Java Web Start console has been activated

The windows (application window and JWS console window) get
closed, but the process (javaw.exe) never terminates and
the shutdownhook never gets called.

This happends with JDK 1.4.1 and JWS 1.2.

With the following combinations, the bug does not occur,
i.e. the JVM terminates normally:

    JDK 1.4.0 and JWS 1.2
    JDK 1.4.1 and JWS 1.0.1
    JDK 1.4.0 and JWS 1.0.1

A very problematic side-effect of this is :
If the application is an RMI-server, any RMI-call will
indefinitly block the caller, i.e. the RMI-call never
returns and never throws an exception.

I was unable to reproduce this on Solaris with any combination
of jvm and javaws.

REGRESSION.  Last worked in version 1.0.1

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. turn on "Display Java Console" in Java Web Start
2. run the "sdh" (see below) application via Java Web Start
3. hit the big exit button (or close the window)
4. check if the process is still running

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected : JVM must terminate (javaw.exe must terminate)
Actual   : JVM does not terminate


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package sdh;

import java.util.Date;
import java.io.*;

public class Application1 {

  public static void fappendLine(String fileName, String line) {
    File f = new File(fileName);
    try {
      f.createNewFile();
      FileWriter fw = new FileWriter(f,true);
      fw.write(line+System.getProperty("line.separator"));
      fw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    // add shutdownhook
    Runtime.getRuntime().addShutdownHook(
      new Thread() {
        public void run() {
          System.out.println("Shutdown-hook activated");
          fappendLine("c:\\sdh.txt","Shutdown-hook activated");
          try {Thread.sleep(3000);} catch(Exception e) {} // delay shutdown 3s
        }
      }
    );
    Frame1 frame = new Frame1();
    frame.setSize(300,300);
    frame.validate();
    frame.setVisible(true);
  }
}

--------------------------------------------------------------------

package sdh;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Frame1 extends JFrame {

  public Frame1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    JButton b = new JButton("Exit");
    b.addActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          System.exit(0);
        }
      }
    );
    getContentPane().add(b);
  }

  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
Don't use the Java Web Start Console or close it before
exiting the application.
(Review ID: 166366) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mantis-beta FIXED IN: mantis-beta INTEGRATED IN: mantis-b13 mantis-beta
31-08-2004

EVALUATION This needs to be fixed for mantis ###@###.### 2002-11-01 This is caused by the application's System.out.println call in the run method of the thread that got added to the shutDownHook. When the application called system.exit(), the thread in shutDownHook got executed. In the run method, it calls System.out.println(), which is redirected to the JWS console. But at this time the console is already gone, which causes this hanging. The stack trace below shows the problem: (look for the call where it is trying to insert some text into console) Full thread dump Java HotSpot(TM) Client VM (1.4.1_01-b01 mixed mode): "Thread-6" prio=5 tid=0x007CB510 nid=0xef runnable [ba4f000..ba4fd90] at sun.awt.windows.WFramePeer.getState(Native Method) at java.awt.Frame.getExtendedState(Unknown Source) - locked <0306B0E0> (a javax.swing.JFrame) at javax.swing.RepaintManager.addDirtyRegion(Unknown Source) - locked <030B7DB8> (a javax.swing.RepaintManager) at javax.swing.JComponent.repaint(Unknown Source) at java.awt.Component.repaint(Unknown Source) at javax.swing.text.WrappedPlainView.updateChildren(Unknown Source) at javax.swing.text.WrappedPlainView.insertUpdate(Unknown Source) at javax.swing.plaf.basic.BasicTextUI$RootView.insertUpdate(Unknown Sour ce) at javax.swing.plaf.basic.BasicTextUI$UpdateHandler.insertUpdate(Unknown Source) at javax.swing.text.AbstractDocument.fireInsertUpdate(Unknown Source) at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source) at javax.swing.text.AbstractDocument.insertString(Unknown Source) at javax.swing.text.PlainDocument.insertString(Unknown Source) at com.sun.javaws.ui.console.Console.insert(Console.java:236) at com.sun.javaws.ui.console.Console.write(Console.java:211) at java.io.PrintStream.write(Unknown Source) - locked <030D0680> (a java.io.PrintStream) at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source) at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source) at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source) - locked <030D2920> (a java.io.OutputStreamWriter) at java.io.OutputStreamWriter.flushBuffer(Unknown Source) at java.io.PrintStream.write(Unknown Source) - locked <030D0680> (a java.io.PrintStream) at java.io.PrintStream.print(Unknown Source) at java.io.PrintStream.println(Unknown Source) - locked <030D0680> (a java.io.PrintStream) at Application1$1.run(Application1.java:23) "Java2D Disposer" daemon prio=10 tid=0x007B4290 nid=0x15a in Object.wait() [b8ff 000..b8ffd90] at java.lang.Object.wait(Native Method) - waiting on <03150700> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) - locked <03150700> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at sun.java2d.Disposer.run(Unknown Source) at java.lang.Thread.run(Unknown Source) "TimerQueue" daemon prio=5 tid=0x0079E1D0 nid=0x107 in Object.wait() [b4bf000..b 4bfd90] at java.lang.Object.wait(Native Method) - waiting on <030CFC48> (a javax.swing.TimerQueue) at javax.swing.TimerQueue.run(Unknown Source) - locked <030CFC48> (a javax.swing.TimerQueue) at java.lang.Thread.run(Unknown Source) "AWT-EventQueue-0" prio=7 tid=0x00795A50 nid=0x15c in Object.wait() [b47f000..b4 7fd90] at java.lang.Object.wait(Native Method) - waiting on <02BC4A78> (a Application1$1) at java.lang.Thread.join(Unknown Source) - locked <02BC4A78> (a Application1$1) at java.lang.Thread.join(Unknown Source) at java.lang.Shutdown.runHooks(Unknown Source) at java.lang.Shutdown.sequence(Unknown Source) at java.lang.Shutdown.exit(Unknown Source) - locked <06BD8178> (a java.lang.Class) at java.lang.Runtime.exit(Unknown Source) at java.lang.System.exit(Unknown Source) at Frame1$1.actionPerformed(Frame1.java:13) at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(Unknow n Source) at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.setPressed(Unknown Source) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Sour ce) at java.awt.Component.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Window.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source) "AWT-Shutdown" prio=5 tid=0x00795D40 nid=0x150 in Object.wait() [b43f000..b43fd9 0] at java.lang.Object.wait(Native Method) - waiting on <03058008> (a java.lang.Object) at java.lang.Object.wait(Unknown Source) at sun.awt.AWTAutoShutdown.run(Unknown Source) - locked <03058008> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) "DestroyJavaVM" prio=5 tid=0x0077FAA0 nid=0x15d waiting on condition [0..6fadc] "Signal Dispatcher" daemon prio=10 tid=0x0076B2A0 nid=0x139 waiting on condition [0..0] "Finalizer" daemon prio=9 tid=0x00767240 nid=0x11f in Object.wait() [ac8f000..ac 8fd90] at java.lang.Object.wait(Native Method) - waiting on <0303F550> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) - locked <0303F550> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(Unknown Source) at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source) "Reference Handler" daemon prio=10 tid=0x00767EA0 nid=0x169 in Object.wait() [ac 4f000..ac4fd90] at java.lang.Object.wait(Native Method) - waiting on <0303F5B8> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Unknown Source) at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source) - locked <0303F5B8> (a java.lang.ref.Reference$Lock) "VM Thread" prio=5 tid=0x007651F0 nid=0x9a runnable "VM Periodic Task Thread" prio=10 tid=0x0076A2D0 nid=0xe8 waiting on condition "Suspend Checker Thread" prio=10 tid=0x0076A1A0 nid=0x149 runnable I don't think we can fix this problem in Java Web Start, as adding another shutdownhook in javaws to remove the redirected system.out.println won't work, since there is no gurantee on which shutdownhook thread will be run first (javaws or the application). The shutdownhook's threads are store in a hashset (in java.lang.shutdown), and there is no gurantees as to the iteration order of the set. It worked in 1.4.0 - probably some implementation changes in the JRE 1.4.1 exposed this problem. If the system.out.println call is removed in the run method of the thread added to the shutdownhook, everything works fine. ###@###.### 2002-11-01 fixed in Mantis by using SwingUtilities.invokeLater for updating the console. ###@###.### 2002-11-05 we backed out the fix due to bug 4780174: REGRESSION: JOptionPane deadlocks Java Web Start in Mantis beta ###@###.### 2002-11-18 removing mantis commitment untill swing fixes 4780174: ###@###.### 2002-12-06 Now that swing is proposing fix for 4780174, we would like to restore this fix. ###@###.### 2002-12-16
06-12-2002

WORK AROUND don't call system.out.println in the thread for addShutdownHook - log the output to a file will work ###@###.### 2002-11-01
01-11-2002