JDK-6542185 : Threading issues with java.awt.EventQueue.push/pop
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux
  • CPU: x86
  • Submitted: 2007-04-03
  • Updated: 2010-04-04
  • Resolved: 2007-04-24
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Linux gorm 2.6.19.1-mactel.mactel-061225 #1 SMP PREEMPT Mon Dec 25 19:17:52 CET
2006 i686 GNU/Linux

or

Linux novo 2.4.21-37.0.1.ELsmp #1 SMP Wed Jan 11 18:44:17 EST 2006 i686 i686 i386 GNU/Linux

or

Linux dio 2.6.9-42.0.3.ELsmp #1 SMP Mon Sep 25 17:28:02 EDT 2006 i686
i686 i386 GNU/Linux

or

Linux io 2.6.11.4-21.11-smp #1 SMP Thu Feb 2 20:54:26 UTC 2006 i686 i686 i386 GNU/Linux

EXTRA RELEVANT SYSTEM CONFIGURATION :
It seems that the bug is more likely on multi processor/core machines.

A DESCRIPTION OF THE PROBLEM :
Our product currently suffers from a threading issue in conjunction with java.awt.EventQueue.push/pop. We have been able to produce a small example showing the problem (see below). The bug mainly occurs on multi processor/core machines (we are working with Linux). We observed that whenever an own event queue is pushed a new thread is created (see "Before close()" output). After the pop of that own event queue this additional thread should go away which doesn't happen in all cases (see "After close()" output). In cases where more than one AWT-EventQueue thread exists this sometimes leads to a NPE or a deadlock (see examples below). This bug seems to be an incarnation of bug 6424157 (unfortunately the space for comments is to limited to post all the information given in this report).

NPE:
Before close()
   Thread[AWT-EventQueue-0,6,main]  id = 10
   Thread[AWT-EventQueue-1,6,main]  id = 13
After close()
   Thread[AWT-EventQueue-0,6,main]  id = 10
   Thread[AWT-EventQueue-0,6,main]  id = 14
Error: More than one AWT-EventQueue exists.

Before close()
   Thread[AWT-EventQueue-0,6,main]  id = 14
   Thread[AWT-EventQueue-2,6,main]  id = 15
After close()
   Thread[AWT-EventQueue-0,6,main]  id = 14
   Thread[AWT-EventQueue-0,6,main]  id = 16
Error: More than one AWT-EventQueue exists.

Before close()
   Thread[AWT-EventQueue-0,6,main]  id = 16
   Thread[AWT-EventQueue-3,6,main]  id = 17
After close()
   Thread[AWT-EventQueue-0,6,main]  id = 16
   Thread[AWT-EventQueue-0,6,main]  id = 18
   Thread[AWT-EventQueue-3,6,main]  id = 19
Error: More than one AWT-EventQueue exists.

Exception in thread "AWT-EventQueue-3" java.lang.NullPointerException
        at javax.swing.SwingUtilities.computeIntersection(SwingUtilities.java:473)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:721)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:683)
        at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:663)
        at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:128)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)



Deadlock:
Name: AWT-EventQueue-11
State: BLOCKED on EventQueueFailure$MyEventQueue@12d8ecd owned by: AWT-EventQueue-7
  Total blocked: 1  Total waited: 0
Stack trace:
java.awt.EventQueue.getNextEvent(EventQueue.java:466)
java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:245)
java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

Name: AWT-EventQueue-7
State: BLOCKED on java.awt.EventQueue@40ece0 owned by: AWT-XAWT
  Total blocked: 2  Total waited: 44
Stack trace:
java.awt.EventQueue.postEventPrivate(EventQueue.java:190)
java.awt.EventQueue.postEvent(EventQueue.java:175)
sun.awt.PostEventQueue.flush(SunToolkit.java:1915)
   - locked sun.awt.PostEventQueue@4f7bc2
sun.awt.SunToolkit.flushPendingEvents(SunToolkit.java:624)
java.awt.EventQueue.getNextEvent(EventQueue.java:465)
java.awt.EventQueue.push(EventQueue.java:702)
   - locked EventQueueFailure$MyEventQueue@12d8ecd
   - locked EventQueueFailure$MyEventQueue@11a0d35
EventQueueFailure$ButtonAction.actionPerformed(EventQueueFailure.java:65)
javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
java.awt.Component.processMouseEvent(Component.java:6038)
javax.swing.JComponent.processMouseEvent(JComponent.java:3260)
java.awt.Component.processEvent(Component.java:5803)
java.awt.Container.processEvent(Container.java:2058)
java.awt.Component.dispatchEventImpl(Component.java:4410)
java.awt.Container.dispatchEventImpl(Container.java:2116)
java.awt.Component.dispatchEvent(Component.java:4240)
java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4322)
java.awt.LightweightDispatcher.processMouseEvent(Container.java:3986)
java.awt.LightweightDispatcher.dispatchEvent(Container.java:3916)
java.awt.Container.dispatchEventImpl(Container.java:2102)
java.awt.Window.dispatchEventImpl(Window.java:2429)
java.awt.Component.dispatchEvent(Component.java:4240)
java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

Name: AWT-XAWT
State: BLOCKED on EventQueueFailure$MyEventQueue@11a0d35 owned by: AWT-EventQueue-7
  Total blocked: 2  Total waited: 111
Stack trace:
java.awt.EventQueue.wakeup(EventQueue.java:1009)
java.awt.EventQueue.wakeup(EventQueue.java:1011)
   - locked java.awt.EventQueue@40ece0
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
sun.awt.SunToolkit.wakeupEventQueue(SunToolkit.java:372)
sun.awt.PostEventQueue.postEvent(SunToolkit.java:1936)
sun.awt.SunToolkit.postEvent(SunToolkit.java:589)
sun.awt.X11.XWindow.postEvent(XWindow.java:366)
sun.awt.X11.XWindow.postEventToEventQueue(XWindow.java:377)
sun.awt.X11.XWindow.handleMotionNotify(XWindow.java:739)
sun.awt.X11.XBaseWindow.dispatchEvent(XBaseWindow.java:1100)
sun.awt.X11.XBaseWindow.dispatchToWindow(XBaseWindow.java:1069)
sun.awt.X11.XToolkit.dispatchEvent(XToolkit.java:468)
sun.awt.X11.XToolkit.run(XToolkit.java:626)
sun.awt.X11.XToolkit.run(XToolkit.java:560)
java.lang.Thread.run(Thread.java:619)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
In order to reproduce the problem please compile an run the example as follows:

javac EventQueueFailure.java
java EventQueueFailure

When the GUI is shown please press the "Push Me" button.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No NPE or deadlocks.
ACTUAL -
NPE thrown or deadlock.

REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
EventQueueFailure.java:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

public class EventQueueFailure extends JPanel {
	static JButton anotherButton;
    public EventQueueFailure() {
        add(new JButton(new ButtonAction()));
	anotherButton = new JButton("Threads");
	anotherButton.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			ButtonAction.printAWTThreads("button action");
		}
	});
	
	add(anotherButton);
    }
    
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    private static void createAndShowGUI() {
        JFrame frame = new JFrame("EventQueueFailure");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        EventQueueFailure panel = new EventQueueFailure();
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }
    
    private static final class ButtonAction extends AbstractAction {
        public ButtonAction() {
            putValue(Action.NAME, "Push Me");
        }
        
        public void actionPerformed(ActionEvent e) {
	    anotherButton.setText("active");
            SimpleProgressDialog dialog = new SimpleProgressDialog();
            dialog.pack();
            dialog.setVisible(true);
            MyEventQueue myEventQueue = new MyEventQueue();
            Toolkit.getDefaultToolkit().getSystemEventQueue().push(myEventQueue);
             for (int i = 0; i < 10; i++) {
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                EventQueue.invokeLater(new ProgressSetter(dialog, i));
            }
            printAWTThreads("Before close()");
	    anotherButton.setText("waiting");
            myEventQueue.close();
            if (printAWTThreads("After close()") > 1) {
                System.err.println("Error: More than one AWT-EventQueue exists.\n");
            } else {
                System.out.println();
            }
	    anotherButton.setText("suspend");
            dialog.setVisible(false);
        }
        
        private static List<Thread> getAllThreads() {
            ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
            while (root.getParent() != null) {
                root = root.getParent();
            }
            Thread[] threads = new Thread[500];
            int numThreads = root.enumerate(threads, true);
            List<Thread> list = new LinkedList<Thread>();
            for (int i = 0; i < numThreads; i++) {
                list.add(threads[i]);
            }
            return list;
        }
        
        private static int printAWTThreads(String msg) {
            System.out.println(msg);
            List<Thread> threads = getAllThreads();
            int numAWTThreads = 0;
            for (Thread thread : threads) {
                if (thread.toString().contains("AWT-EventQueue")) {
                    System.out.println("   "+thread.toString()+
                            "  id = "+thread.getId());
                    numAWTThreads++;
                }
            }
            return numAWTThreads;
        }
    }
    
    private static final class MyEventQueue extends EventQueue {
        public MyEventQueue() {
        }
        
        public void close() {
            super.pop();
        }
    }
    
    private static final class SimpleProgressDialog extends JDialog {
        private JProgressBar progressBar;
        private JLabel progressLabel;
        public SimpleProgressDialog() {
            progressBar = new JProgressBar();
            progressBar.setMinimum(0);
            progressBar.setMaximum(10);
            progressLabel = new JLabel("Performing work ...");
            JPanel panel = new JPanel(new BorderLayout());
            panel.add(progressBar, BorderLayout.CENTER);
            panel.add(progressLabel, BorderLayout.SOUTH);
            getContentPane().add(panel);
        }
        
        public void setProgress(int progress) {
            progressBar.setValue(progress);
        }
    }
    
    private static final class ProgressSetter implements Runnable {
        private SimpleProgressDialog dialog;
        private int progress;
        
        public ProgressSetter(SimpleProgressDialog dialog, int progress) {
            this.dialog = dialog;
            this.progress = progress;
        }
        
        public void run() {
            dialog.setProgress(progress);
        }
    }
}
---------- END SOURCE ----------

Comments
WORK AROUND If you want to perform long operation and get notification upon its completion user should use SwingWorker class (or similar aproach) I.e. the test should not kill EDT, but move long operation to another thread and send notification to EDT upon complition of the operation.
04-04-2007

EVALUATION Wel, the deadlock described in the CR is a know bug (4943231 (Regression test EventQueue/PushPopDeadlock fails)). As for observation I think it is expected behavior :( The test in actionPerformed() (on even dispatch thread EDT1 associated with first event queue (EQ1)) creates second event queue (EQ2) calls EventQueue.push() with it. Then it calls EventQueue.invokeLater() which posts event to EQ2 and create event dispatch thread (EDT2) for it. Later the code (in the same actionPerformed()) pops EQ2 out of the stack. And at the end it expects to have just one thread with "AWT-EventQueue..." name. But let's count what do we have. We have EDT1 (because we still in the same actionPerformed() and so this thread still alive), we may have EDT2 because it's processing the last event it started before we call pop(), and we may have EDT3 which is new event dispatch thread for EQ1 (current implementation of push()/pop() does't reuse old EDT (see 6424157 for more details)). So, it is expectable to have more then one thread which name starts from "AWT-EventQueue". Imho the main problem of the test is that it uses push()/pop() in case when you should not use it. If you want to perform long operation and get notification upon its completion user should use SwingWorker class (or similar aproach) I.e. the test should not kill EDT, but move long operation to another thread and send notification to EDT upon complition of the operation. So, we have two ther bugs for real problems described here, but main problem descibed here is not a bug. Thus I'm closing this bug as not a bug.
04-04-2007