views:

100

answers:

1

I have an application that is unresponsive and seems to be in a deadlock or something like a deadlock. See the two threads below. Notice that the My-Thread@101c thread blocks AWT-EventQueue-0@301. However, My-Thread has just called java.awt.EventQueue.invokeAndWait(). So AWT-EventQueue-0 blocks My-Thread (I believe).

My-Thread@101c, priority=5, in group 'main', status: 'WAIT'
     blocks AWT-EventQueue-0@301
      at java.lang.Object.wait(Object.java:-1)
      at java.lang.Object.wait(Object.java:485)
      at java.awt.EventQueue.invokeAndWait(Unknown Source:-1)
      at javax.swing.SwingUtilities.invokeAndWait(Unknown Source:-1)
      at com.acme.ui.ViewBuilder.renderOnEDT(ViewBuilder.java:157)
        .
        .
        .
      at com.acme.util.Job.run(Job.java:425)
      at java.lang.Thread.run(Unknown Source:-1)

AWT-EventQueue-0@301, priority=6, in group 'main', status: 'MONITOR'
     waiting for My-Thread@101c
      at com.acme.persistence.TransactionalSystemImpl.executeImpl(TransactionalSystemImpl.java:134)
        .
        .
        .
      at com.acme.ui.components.MyTextAreaComponent$MyDocumentListener.insertUpdate(MyTextAreaComponent.java:916)
      at javax.swing.text.AbstractDocument.fireInsertUpdate(Unknown Source:-1)
      at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source:-1)
      at javax.swing.text.AbstractDocument$DefaultFilterBypass.replace(Unknown Source:-1)
      at javax.swing.text.DocumentFilter.replace(Unknown Source:-1)
      at com.acme.ui.components.FilteredDocument$InputDocumentFilter.replace(FilteredDocument.java:204)
      at javax.swing.text.AbstractDocument.replace(Unknown Source:-1)
      at javax.swing.text.JTextComponent.replaceSelection(Unknown Source:-1)
      at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(Unknown Source:-1)
      at javax.swing.SwingUtilities.notifyAction(Unknown Source:-1)
      at javax.swing.JComponent.processKeyBinding(Unknown Source:-1)
      at javax.swing.JComponent.processKeyBindings(Unknown Source:-1)
      at javax.swing.JComponent.processKeyEvent(Unknown Source:-1)
      at java.awt.Component.processEvent(Unknown Source:-1)
      at java.awt.Container.processEvent(Unknown Source:-1)
      at java.awt.Component.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Container.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Component.dispatchEvent(Unknown Source:-1)
      at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source:-1)
      at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source:-1)
      at java.awt.Component.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Container.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Window.dispatchEventImpl(Unknown Source:-1)
      at java.awt.Component.dispatchEvent(Unknown Source:-1)
      at java.awt.EventQueue.dispatchEvent(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEvents(Unknown Source:-1)
      at java.awt.EventDispatchThread.pumpEvents(Unknown Source:-1)
      at java.awt.EventDispatchThread.run(Unknown Source:-1)

Here is the TransactionalSystemImpl.executeImpl method:

private synchronized Object executeImpl(Transaction xact, boolean commit) {
    final Object result;

    try {
        if (commit) { // this is line 134
            clock.latch();
            synchronized(pendingEntries) {
                if (xactLatchCount > 0) {
                    pendingEntries.add(xact);
                } else {
                    xactLog.write(new TransactionEntry(xact, clock.time()));
                }
            }
        }

        final TransactionExecutor executor = transactionExecutorFactory.create(
                xact.getClass().getSimpleName()
        );

        if (executor == null) {
            throw new IllegalStateException("Failed to create transaction executor for transaction: " + xact.getClass().getName());
        }

        result = executor.execute(xact);

    } finally {
        if (commit) clock.unlatch();
    }

    return result;
}

Does anyone know what's going on here or how to fix it?

+1  A: 

It's hard to tell without seeing the code, but from the stack trace, it looks like you're firing some sort of transactional code from the event dispatch thread. Does that code then kick off an instance of My-Thread? The EDT could be blocked waiting for My-Thread from within the transactional code, but My-Thread can't finish because it needs the EDT.

If this is the case, you can use SwingUtilities.invokeLater for your rendering so the EDT finishes the transactional code and then it will render the updates. Or, you can not perform the transactional code from the EDT. For actual work that's not related to rendering, you should use a SwingWorker to avoid doing any heavy processing on the EDT.

Jeff Storey
No, the transactional code is not kicking off an instance of My-Thread. That is already running at the time.
Paul Reiners
Ok. Another thing to check. Are you sure the call to ViewBuilder.renderOnEDT is not occurring from the EDT too (since you probably wouldn't want to call invokeAndWait) from the EDT.Or, is it possible that the call to renderOnEDT is synchronized on something? Then if the EDT tries to synchronize on that same object (in the 2nd stack trace you posted) while My-Thread is in invokeAndWait, you will have a deadlock.
Jeff Storey
ViewBuilder.renderOnEDT is sometimes called on the EDT, but, in that case, we don't use invokeAndWait: if (SwingUtilities.isEventDispatchThread()) { container = renderable.render(); } else { try { SwingUtilities.invokeAndWait(renderable); } catch (Exception e) { throw new RuntimeException(e); } container = renderable.getContainer(); }
Paul Reiners
Yes, renderOnEDT is synchronized on something way up in the call stack, the com.acme.persistence.TransactionalSystemImpl.executeImpl method which is synchronized. And renderOnEDT is waiting to enter that same method. So, that is the source of the deadlock it looks like. Now I have to figure out how to fix it.
Paul Reiners
Again without knowing the whole code, I can't provide many more details. But in general, you should avoid synchronizing and long running operations on the EDT. So, using invokeLater should help run the operation on the EDT without blocking it. Also as I mentioned, you should look into using SwingWorker and not doing the transactions on the EDT.
Jeff Storey