Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
J2SE Version (please include all output from java -version flag): java version "1.6.0-beta2" Java(TM) SE Runtime Environment (build 1.6.0-beta2-b81) Java HotSpot(TM) Client VM (build 1.6.0-beta2-b81, mixed mode, sharing) java version "1.5.0_07" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-b02) Java HotSpot(TM) Client VM (build 1.5.0_07-b02, mixed mode, sharing) Does this problem occur on J2SE 1.4.x or 5.0.x ? Yes, the issue is a general problem, which concers Tiger and Mustang. Operating System Configuration Information (be specific): Windows XP SP2 Professional / DE Hardware Configuration Information (be specific): Intel Pentium Mobile on VAIO VGN-FS 1.6 GHz Bug Description: java.awt.EventQueue push/pop might cause threading issues Steps to Reproduce (be specific): The issue rather concerns a general problem of the java.awt.EventQueue class than a specific bug: The following typical scenario might occur in a Swing application: 1. Assume the following EventQueue utility class: public class EventPump extends java.awt.EventQueue { private static final EventQueue EVENTQUEUE = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue(); public static EventPump push() { EventPump pump = new EventPump(); EVENTQUEUE.push(pump); return pump; } protected void pop() { try { super.pop(); } catch (Exception ex) { ex.printStackTrace(); } } } 2. Assume that the application wants to execute some code on a separate thread invoked from inside the awt thread. In addition, the current awt thread should pause for the separate thread to terminate, i.e. something like ... // here we assume being on the awt thread Thread worker = new Thread() { public void run() { // do something ... } } worker.start(); worker.join(); // here the awt thread is waiting for the worker 3. In order NOT to freeze the gui, the code snippet 2. would be extended using the utility 1. above: Thread worker = new Thread() { public void run() { // do something ... } } worker.start(); EventPump keepOnGoing = EventPump.push(); worker.join(); keepOnGoing.pop(). 4. Now, looking at 3. and also having the source code of java.awt.EventQueue and java.awt.EventDispatchThread in mind, we can make several observations. Note that the implementation is in principle the same for Tiger and Mustang concering the push, pop, initDispatchThread, stopDispatching, stopDispatchingLater etc. methods relevant for the subsequent threading dicussion. Hence, the following notes apply to both Java versions. a. When executing push(), the following happens: a.i. A new EventQueue Q2 is pushed on top of the EventQueue stack. a.ii. All pending events are transferred from the current EventQueue Q1 to Q2. a.iii. The thread T1 of the original EventQueue is stopped lazily, which is implemented by setting the boolean doDispatch flag, which finally terminates the thread's run() method when the thread the next time is trying to pump an event. a.iv. T1 is detached from Q1 by executing the theQueue.detachThread() method. a.v. For instance, T1 is still running because it is currently dispatching scenario 3. above and will execute the join() method first. a.vi. Once, a new event is arriving on the currently active EventQueue Q2, a new awt thread T2 is created for Q2, which starts pumping events on Q2. b. Once, the worker thread of scenario 3. above has terminated, the pop() method is executed by T1, which has the following effect: b.i. All pending events are transferred from Q2 to Q1. b.ii. The current dispatch Thread T2 is stopped by T1, i.e. b putting a so-called StopEvent into Q2, which indirectly modifies the above-cited doDispatch flag, when the StopEvent is dispatched by T2. Note, this time T1 is waiting for this to happen. Now, when Q1 resumes pumping events, it has no current dispatch thread due to item a.iv above. Consequently, a new thread T1* is created for Q1, which gets the new active awt thread. Note, the javax.swing.SwingUtilities.isEventDispatchThread() method will correctly supply "true" if and only if now called from T1*. Hence, the method would correctly evaluate to "false" when called by T1 immeadiately after the pop() method call of the avove-cited scenario. Problem: Immediately, before scenario 3. terminates, we have two active threads (with the same name), i.e. T1*, the new awt thread, and T1 the original awt thread. T1 is still dispatching its event, which might hence interfere with newly arriving events dispatched by T1*. Clearly, speaking there is room for potential threading issues for the period between the start of T1* (after calling pop() above) and the dead of T1. Imagine that T1 modifies variables before dying. Such changes would only be visible to T1* when properly synchronized. By constrast, developers relying on the "only one dispatch thread at the time" paradigm would probably not see a reason for synchronization in this context. Proposed Solution: Instead of detaching T1 from its queue in a.iv above and stopping it, it might be better (also for performance and resource aspects) , NOT to detach an awt thread when pushing a new EventQueue. The awt thread should just be lazily stopped and hence have a chance to resume dispatching on its original queue, when pop() is called before lazy stopping really happens. As a consequnce, the original queue would keep its original awt thread when resuming work in the above-cited scenario, which seems to be a typical EventQueue push/pop use case. The original awt thread could continue pumping and there would be no need for synchronization. In addition, the SwingUtilities.isEventDispatchThread() would continue to supply "true" for T1 in the above-cited scenario after pop(), which might be a more comprehensive behavior from the user's point of view.
|