Friday, April 2, 2010

EDT deadlock

Many swing developers are probably unconsciously on the it-won't-happen-to-me side on this one, but I am the living proof that these things happen ... ok, once in a year. The bad part is that it can manifest for your clients running the latest Windows 2008 build, while your XP buddy will behave ok.

This is one of the situations when a debugger can't help you out cause your app is frozen, so ... meet thread dumps. Because most swing apps are consoleless, Ctrl+Break or Ctrl+/ won't work. But your smart IDE should help you take thread dumps; alternatively use <JAVA_HOME>/bin/jstack.

When your application freezes, you might be experiencing a deadlock, ex. when 2 or more threads are blocking each other, or it could be a temporary blockage due to resource unavailability. Let's go for the first case in this post.

Finding the buggy code turns out to be very easy: for a thread dump generated by the Sun JVM, start by looking for the string BLOCKED in the thread dump.

Immediately you can see the lock that this thread is trying to acquire, search for it in the rest of the thread dump.

It turns out the same lock was acquired previously, and in this case by the EDT. Moreover, the lock that the misbehaved "NGUI Thread Pool-2" thread is trying to acquire is a core swing lock.

The fix in such cases is (almost?) always making the code that tries to acquire the lock run on the EDT: go to the misbehaving thread, then backwards on the stacktrace until you can recognize some of your code. Then dig through it and decide on the place where the flow should continue on the EDT instead of your own (background) thread. You do the magic by wrapping your call like this:
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        <your call>    }
If you want to avoid such headaches altogether in the future, then play by the book ... and the book says:

Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors. Some Swing component methods are labeled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.