Wednesday, February 11, 2009

InterruptedIOException considered harmful


About a year ago I patched my own log4j to fix the fact that it can swallowInterruptedException via InterruptedIOException. Then I filed a bug against log4j, and got into an email conversation with Curt Arnold, who rightly pointed out that there were other scenarios where InterruptedException could be easily ignored. Anything that wraps an InterruptedException or InterruptedIOException and rethrows something that doesn’t derive from them is effectively ignoring the intended effect of a thread interrupt. The most common examples of this arejava.lang.reflect.Method.invoke andjava.lang.reflect.Constructor.newInstance which both throw java.lang.reflect.InvocationTargetException, which can have this problem.
The title of this post may be a bit misleading; java.io.InterruptedIOExceptiondoesn’t cause this problem, it just makes it at least twice as difficult, because you must always check for both InterruptedException and InterruptedIOException in wrapped exceptions. But it also means that any method that throwsjava.io.IOException must have special handlers for an interrupted thread.
I tried to find a bug in Sun’s database that warns about InterruptedIOExceptionand these cases, but the closest I could find was Sun bug 4385444. That doesn’t really have anything to do with it.
When I first saw the changes made for java.nio for interruptible IO, I thought the use of InterruptedIOException was clever and elegant. But because of the very special nature of InterruptedException, I changed my mind—of course, there wasn’t much option, because java.nio integrates with existing java.io interfaces and methods which already do not throw InterruptedException, therefore they had to follow that path. It’s really a difficult situation; it isn’t the first case in Java of a hidden or wrapped InterruptedException, it just makes it more widespread. Now you have to handle an interrupted thread anywhere java.io.IOException is thrown.
Note that on Solaris (x86 and Sparc) java.io methods can also throwInterruptedIOException. You don’t need to use java.nio to see this effect. On other platforms you only have to worry about this if you (or things you call) usejava.nio.
I’ve come to the conclusion that thread interrupts are so special, and currently so difficult to deal with, that Java should treat InterruptedException as a third type of exception: one that is implicitly thrown from every method. Of course this opens up its own can of worms, not to mention that it’s about 15 years too late to make such a change.
This also speaks to the fact that you should be following a pattern where it’s rare that you catch exceptions that you don’t understand, and should be catching them as far up as possible, where you can centralize your exception handling. I think this is also a good case for frameworks like Spring and Seam which use AoP; each method invocation can have a carefully thought out exception handler, either via your own AoP, or directly handled in the framework.
Update I’ve found bug 4176863 which is related to this issue, but more importantly, the paper Java Thread Primitive Deprecation. I had read this long, long ago, and thought it important to link here.