JDK-4030718 : A program which calls Toolkit.getDefaultToolkit() won't terminate.
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version:
    1.1,1.1.1,1.1.2,1.1.3,1.1.4,1.1.5,1.1.6,1.2.0,1.2.1,1.2.2,1.3.0,1.3.1 1.1,1.1.1,1.1.2,1.1.3,1.1.4,1.1.5,1.1.6,1.2.0,1.2.1,1.2.2,1.3.0,1.3.1
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,solaris_2,solaris_2.4,solaris_2.5,solaris_2.5.1,solaris_2.6,solaris_7,windows_95,windows_98,windows_nt,windows_2000 generic,solaris_2,solaris_2.4,solaris_2.5,solaris_2.5.1,solaris_2.6,solaris_7,windows_95,windows_98,windows_nt,windows_2000
  • CPU: generic,unknown,x86,sparc
  • Submitted: 1997-02-06
  • Updated: 2000-05-24
  • Resolved: 2000-05-24
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.0 betaFixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Name: mc57594			Date: 02/06/97

A program which calls the static method getDefaultToolkit()in
class Toolkit won't terminate. The following is a simplest
java source code for this bug.

import java.awt.*;                                                          
class Test {                                    
    public static void main(String[] args) { 
        Toolkit.getDefaultToolkit();            
    }                        
}         

After starting this program, it should return to OS. However,
it hangs there forever.

I use Window 95. I have tried this program on several different
machines, e.g, standalone ones and network-attached ones, the 
problem is the same.                                                                                                                                            
company  -  dept of comp. sci. King's College London  , email  -  ###@###.###
======================================================================

Name: tb29552			Date: 02/08/99


When java.awt.SystemColor is loaded, it calls Toolkit.getDefaultToolkit(), which
creates two AWT threads.  These threads never stop, and they are not daemon
threads, so the program hangs.  

The following code demonstrates this problem: 

public class SimplCon {
	public SimplCon() {
	}

    static public void main(String args[]) {
        java.awt.Color color = java.awt.SystemColor.window;
    }
}


Note: I have reproduced this problem on a Windows 98 and NT 4.0
machine. It only happens when you use JDK 1.2. It works fine in JDK 1.1.7A.
======================================================================

Name: vi73552			Date: 06/14/99


The AWT threads are *still* not Daemon threads (reported as bug 4030718).
I have presented Sun with a fix for this, which they've had for 7 months.
It's not that the problem is not fixable - so how come no-one has even
evaluated the bug/fix? The report shows up as useless.
(Review ID: 84317)
======================================================================

Name: tb29552			Date: 04/03/2000


java version "1.3.0rc2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc2-Y)
Java HotSpot(TM) Client VM (build 1.3.0rc2-Y, interpreted mode)

In developing a system which needs to shutdown the grapics environment, I have
found that it is impossible to cleanly close down the awt. The natively
implemented  event loop thread never checks for Java exceptions or any other
exit condition, and as Thread.stop() is now deprecated, it is impossible to
cleanly bring down the display. Here is the excerpt from awt_Motif.c as it
stands - the possiblity of exitting has been considered by the authors, but not
implemented...

...
    /*
     * ACTUALLY PROCESS EVENTS
     */
    
    while(True) {
        ...
	waitForEvents(env, fdXPipe, AWT_READPIPE);
        ...
    } /* while(True) */

    /* If we ever exit the loop, must unlock the toolkit */
...
(Review ID: 103193)
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: merlin FIXED IN: merlin-beta INTEGRATED IN: merlin-beta
14-06-2004

EVALUATION Commit for Kestrel. eric.hawkes@eng 1999-06-22 Any fix which involves changing the EventDispatchThread, PostEventQueue, and Toolkit threads to daemon suffers from a major design problem. Recall that a new, child thread inherits the daemon status of its parent thread. By making the EventDispatchThread daemon, we would change the deafult daemon state of all threads spawned by client code in event handlers. This is a fairly common thing to do (e.g., Swing worker threads). Previously, daemon-ness didn't matter in an AWT application, because the app would never exit anyways. If this bug were fixed, it definitely would matter. That's okay, as long as the default for client code threads is non-daemon. Changing this default breaks the class of applications which relies on the current default daemon state. It is unlikely that every developer has been careful enough to set the daemon status of all of their threads explicitly. We are continuing to consider other options, including minor API changes, more complicated fixes, and command-line switches. david.mendenhall@eng 1999-09-10 Customer indicates that their product will ship before Kestrel is released. It appears that more substantial changes are necessary to address this issue correctly. We will continue to investigate this for Merlin. eric.hawkes@eng 1999-10-05 Another report that is similar: 4291432 should also be addressed for Merlin. eric.hawkes@eng 1999-11-16 I am recategorizing this as an RFE, since it is a request for a rearchitecture of the AWT, and requires new public APIs. We have committed to fix this in Merlin. eric.hawkes@eng 2000-03-08 Apparently, this fix was broken late in Merlin. 4515058 has been filed about it. It is probably too late to fix the problem in Merlin, but we will try to fix it as soon as we can. ###@###.### 2001-11-09 No, I'm wrong. It isn't broken. We just found one case that doesn't exit. All the other tests I tried exit just fine. ###@###.### 2001-11-15 The solution is 1) to make the toolkit thread daemon and 2) to start an AWT event dispatch thread only when the first java event appears in its associated event queue and 3) to stop all the event dispatch threads when all three conditions become true: 1. There are no displayable AWT or Swing components. 2. There are no events in the native event queue. 3. There are no events in java event queues. When all the event dispatch threads are terminated, the VM will exit if there are no other alive non-daemon threads. This solution implies that normally if an application uses AWT, the only legal cases for VM to not terminate are as follows: 1. There is an alive non-daemon thread that doesn't belong to AWT (see Thread.isAlive() and Thread.isDaemon()). 2. There is a displayable component (see Component.isDisplayable()). 3. An AWT event is processed on one of AWT event dispatch threads. To write an AWT application that will terminate without System.exit(), one should do the following: 1. Stop all non-daemon threads that the application has started. 2. Make all components undisplayable. 3. Make sure that the application doesn't continuously post synthetic AWT events to the AWT event queue or that it doesn't post an AWT event which processing cannot complete (e.g. an ActiveEvent that starts an infinite loop in its dispatch() method). ###@###.### ###@###.### 2002-04-15
11-06-2004

WORK AROUND Name: tb29552 Date: 10/27/99 Call System.exit() when it is time to shut down the app. (Review ID: 97019) ======================================================================
11-06-2004

SUGGESTED FIX Date: Fri, 6 Nov 1998 10:40:57 +0000 (GMT) From: Alex Blewitt <###@###.###> To: Tom Ball <Thomas.Ball@Eng> cc: ###@###.###, ###@###.### Subject: 100% pure way of closing applications MIME-Version: 1.0 Folks, I have written a solution to the problem, and am enclosing it with this e-mail. I have tested it under Solaris 5.6/JDK 1.1 and it seems to work. To demonstrate the problem, I have created a new EventDispatch thread and a new Window class (in alex.awt.*). This demonstrates the feasibility without requiring me to hack around with the internal JVM stuff itself. It uses a blocker thread to prevent the JVM from quitting whilst there is still 'active' processing to be done, but does have the processing thread as a daemon one. Javaworld: I'm copying you on this, because I'm sure that Bill Venners or Mark Johnson would love to write an article on the use of a BlockerThread. It's quite appropriate for command line tasks and other non-AWT related tasks where you need to have a Daemon server thread processing events. I've enclosed just the Java source file (contains 3 classes in alex.awt package). I've written JavaDoc comments for the source code, and checked that it a) compiles b) runs and c) generates nice(ish) JavaDoc stuff about what's going on. (If you're generating JavaDoc, make sure you've got the -private flag on :) To test it: java alex.awt.Example -> creates a test thread, starts it, and will quit (expected) java alex.awt.Example 1 -> creates a window, makes it visible, and will hang (expected) java alex.awt.Example 1 2 -> creates a window, hides it, and will quit (expected) java alex.awt.Example 1 2 3 -> creates an event and puts it in the queue, and will hang (expected) java alex.awt.Example 1 2 3 4 -> creates an event, puts it in the queue, processes it, and quits (expected) There you go. You wanted a solution, you got one :-) I'll be happy to defend this solution against any problems that are presented about it. I will stake my reputation on it working for over 95% of the existing apps :-) NB: Copyright and use of the software are up for grabs to be put in either an article, or merged into Java source code. All I ask is that it is acknowledged somewhere. I look forward to hearing your feedback about this, Alex. /***************************************************************|* Alex Blewitt * Hug, and the world hugs with you *| |* ###@###.### * *| |* Mobile: + 44 966 158647 * Spread a little happiness *| \***************************************************************/ package alex.awt; /** * Example code to show the ExampleWindow objects. * <p> * This tests the principles behind the ExampleDispatchThread, * and easily shows that it is possible to overcome the 'bug' which * stops the JVM from working nicely. * <p> * Usage: * <p> * Run with different numbers of arguments (can be any values) * to see different effects. * <dl> * <dt>No arguments * <dd>Creates and runs the Thread, but doesn't generate any AWT-like events. Quits. * <dt>One argument * <dd>Creates a window and makes it visible. Hangs. * <dt>Two arguments * <dd>Generates a window, shows it, and then closes it. Quits. * <dt>Three arguments * <dd>Generates an event, and puts it in the event queue. Hangs. * <dt>Four arguments * <dd>Generates an event, inserts it into the queue, processes it. Quits. * </dl> */ class Example { public static void main(String args[]) { // system would set up the AWT Thread normally ExampleDispatchThread edt = new ExampleDispatchThread(); edt.setDaemon(true); edt.start(); switch(args.length) { case 0: // quits System.out.println("No windows created; running to end of loop"); break; case 1: // hangs until the 'user' closes it System.out.println("Generating a window and making it visible"); new ExampleWindow().setVisible(true); break; case 2: // quits System.out.println("Generating a window, and closing it again"); ExampleWindow ew = new ExampleWindow(); ew.setVisible(true); ew.setVisible(false); break; case 3: // hangs until 'event' is processed System.out.println("Generating an event, and putting it in the Queue"); edt.insertEvent("an event"); break; case 4: // quits System.out.println("Generating an event, putting it in and processing it"); edt.insertEvent("an event"); edt.removeEvent(); break; } } } /** * This is a representation of a Window object. * (Do not use the real Window to prevent the AWTThread kicking in). * The only important methods that are provided here is the setVisible() * method. Could equally be used for the Constructor/dispose() instead of * the setVisible true/false. Obviously call super.setVisible() if * you're using a real component. */ class ExampleWindow { private static int numberOfVisibleWindows = 0; private boolean visible; /** * Sets the Window to be visible (true) or not (false). When no visible * windows are left, the AWT will quit. */ public void setVisible(boolean visible) { // super.setVisible(); if (this.visible != visible) { // it's changed if (visible) { numberOfVisibleWindows++; ExampleDispatchThread.setVisibleWindows(true); } else { numberOfVisibleWindows--; if (numberOfVisibleWindows == 0) { ExampleDispatchThread.setVisibleWindows(false); } } } this.visible = visible; } } /** * A pseudo DispatchThread. Kind of rolls the event queue and * Thread into one for the purposes of this example, although in * the real AWT they are different. * <p> * Also uses a static based resolver instead of a singleton type * method. Again, to get the point across in a succint a way possible. * @see #anyVisibleWindows * @see #anyRemainingEvents * @see BlockingThread */ class ExampleDispatchThread extends java.lang.Thread { // used to block the JVM from quitting private static BlockingThread blockingThread; // used to store a single event object, for purposes of example private static Object eventQ; // used to store a copy of the # visible windows. Could equally be // well implemented directly using the Window class. private static boolean visibleWindows; /** * Called by the Window class to tell the EventDispatchThread that * there are visible windows. Must do this so that a BlockingThread * is created. * @see #update */ static void setVisibleWindows(boolean visible) { visibleWindows = visible; update(); } /** * Called internally to find out if there are any visible windows. * Could be re-written at a later stage to enquire from the Window * class directly; however, the Window class must still notify the * EDT to let it know when a Window is created/disposed. * @see ExampleWindow * @see #update */ private static boolean anyVisibleWindows() { return visibleWindows; } /** * Called internally to find out if there are any remaining events. * Could be re-written at a later stage to enquire from the EventQueue * class directly; however, the EventQueue class must still notify the * EDT to let it know when an Event is inserted/removed. * @see #update */ private static boolean anyRemainingEvents() { return (eventQ != null); } /** * Inserts a dummy event into the queue. If EventQueue is being used, * must have some kind of callback so that update() can be called. * @see #update */ public static void insertEvent(Object o) { eventQ = o; update(); } /** * Removes (processes) an event from the queue. If EventQueue is being used, * must have some kind of callback so that update() can be called. * @see #update */ public static Object removeEvent() { Object result = eventQ; eventQ = null; update(); return result; } /** * Makes sure that the system only blocks the JVM iff there are no * more events and no more windows. * <p> * Must be called whenever there is a change in anyVisibleWindows() * or anyRemainingEvents(). * @see #anyRemainingEvents * @see #anyVisibleWindows * @see #setVisibleWindows * @see #insertEvent * @see #removeEvent */ private static void update() { if (anyVisibleWindows() || anyRemainingEvents()) { createBlockingThread(); } else { destroyBlockingThread(); } } /** * Helper method to create a BlockingThread. If one exists * already, this method has no effect. * <p> * This method is effectively class synchronized if a blocking thread * does not exist. However, this lock is only obtained if necessary. */ private static void createBlockingThread() { if (blockingThread == null) { synchronized(ExampleDispatchThread.class) { if (blockingThread == null) { blockingThread = new BlockingThread(); blockingThread.start(); yield(); } } } } /** * Helper method to remove the BlockingThread. * Kills it off by sending it the die() request, * and then nullifying the (static) reference. */ private static void destroyBlockingThread() { blockingThread.die(); blockingThread = null; yield(); } /** * Demo processing loop. In a real situation, this * would be calling requests to getNextEvent() or similar. * Currently indefiniately blocks to show that the AWT Daemon * thread is running. */ public void run() { // demo loop; just hangs to prove a point while(true) { try { synchronized(this) { wait(); } } catch (Exception e) { } } } } /** * Blocker thread. * <p> * Once started, this thread will continue to run in the background * using the wait primitive. As a result, will take up minimal resources * once running. * <p> * The purpose of this thread is to prevent the JVM from accidentally * quitting before the time is ripe. Once told to die(), the BlockingThread * will tidy itself up and allow the JVM to quit (providing that there * are no more non-Daemon threads). * <p> * Example: * <pre> * BlockingThread bt = new BlockingThread(); * bt.start(); // will cause the JVM to not quit * // some time later * bt.die(); // the JVM is now free to quit * </pre> * @see #die */ class BlockingThread extends java.lang.Thread { private boolean running = true; /** * Asks this BlockingThread to tidy itself up. * Cleanly finishes by running off the bottom of the * run method, so is lock-safe. */ public void die() { running = false; this.interrupt(); } /** * Runs in a resource friendly perpetual loop. * <p> * Will terminate on two conditions: * <ol> * <li>If an Error is generated whilst the Thread is wait()ing * <li>If the die() method has been called * </ol> */ public void run() { while(running) { try { synchronized(this) { wait(); } } catch (Exception e) { } } } }
11-06-2004