Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6424157

java.awt.EventQueue push/pop might cause threading issues

    XMLWordPrintable

    Details

    • Subcomponent:
    • Resolved In Build:
      b102
    • CPU:
      x86
    • OS:
      windows_xp

      Description

      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.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              art Artem Ananiev
              Reporter:
              tyao Ting-Yun Ingrid Yao (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: