 |
Re: Swing versus SWT Thread Confinement
Posted by robogeek on April 25, 2007 at 02:33 PM | Comments (19)
In Swing versus SWT Thread Confinement is offered a suggestion to make Swing take a behavioral leaf from SWT, and to change Swing so that it "fails fast" when called from a thread other than the event dispatch thread. Since Swing (and SWT) are not multithread safe, if you call Swing methods from outside the event dispatch thread the behavior is "undefined". This is a nice way of saying you're on your own, that your program might work, or it might die in flames, or it might just run weirdly with random behavior. And, I think, some of the frustration over Swing applications might stem from those who call Swing methods from outside the event dispatch thread, which then will have undeterminable behavior.
The simple implementation would be to edit every Swing method inserting checks to see if the caller is not on the EDT and to throw an IllegalStateException.
Until recently I was part of the CCC, an internal committee where we review Java SE changes to look for compatibility and conformance issues. It's very peculiar work reviewing API or other interface changes and trying to imagine how likely it is the change will or will not break applications. What I got from sitting in that group for those 3 years is an enhanced appreciation for maintaining compatibility. Sometimes in the CCC we would preserve some existing behavior, even if it was known to be buggy or a poor choice of behavior, so that we would not break existing applications in the field.
Clearly while it's a poor choice that applications can call Swing methods from outside the EDT, clearly there are applications who do that. And the simple version of the change, to throw exceptions for that case, would break existing applications in the field.
A preferable route is to come up with a way to make the check optional. I agree the check is a good idea, and to be able to set a flag that turns this check on an application author could then run their application with the checking mode enabled and find out when/where their application calls Swing from outside the EDT.
The more serious proposal.. to add an annotation to Swing methods saying they require being run on the EDT, that's a good idea. And I am envisioning it's possible to write a code munger using the Jackpot engine in Netbeans 6 so that it searches for methods having that annotation, and inserts or deletes the checking code.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
What about just using the assert() mechanism?
By default they are off/disabled, but they can be enabled 'in the field' or at development time.
Rgds
Damon
PS. I'm biased (I was on the JSR committee) but I think this is just the sort of thing that they were aimed at: ask Josh.
Posted by: damonhd on April 25, 2007 at 02:44 PM
-
I use a custom RepaintManager to check if any swing method is called outside the EDT and print a stack trace in that case.
Posted by: grashalm on April 25, 2007 at 03:23 PM
-
Until recently I was part of the CCC, an internal committee where we review Java SE changes to look for compatibility and conformance issues. It's very peculiar work reviewing API or other interface changes and trying to imagine how likely it is the change will or will not break applications. What I got from sitting in that group for those 3 years is an enhanced appreciation for maintaining compatibility. Sometimes in the CCC we would preserve some existing behavior, even if it was known to be buggy or a poor choice of behavior, so that we would not break existing applications in the field.
So you're the guilty ones. :|
Posted by: i30817 on April 25, 2007 at 05:02 PM
-
The simple implementation would be to edit every Swing method inserting checks to see if the caller is not on the EDT and to throw an IllegalStateException.
David, actually, I think the simplest implementation is what grashalm mentioned - a custom RepaintManager is the most non-invasive way to do this - this approach would also makes the check optional, and therefore help preserve backward compatibility.
Posted by: vivpr on April 25, 2007 at 08:48 PM
-
I like the annotation idea. These annotations could be documented in Javadoc, making it very clear which Swing methods are safe to call outside the EDT and which are not.
Posted by: prunge on April 25, 2007 at 11:50 PM
-
The solution to these kind of problems already exist. Use a custom repaint manager as stated above. If you want to make it default for say 7.0, when you explicitly say that you start from a 7.0 code base, here's the solution: http://www.javalobby.org/java/forums/t88549.html
It's too bad that explicit compatibility isn't here already since so many bugs could've been solved in a nice way, without compatibility problems with old code.
Cheers,
Mikael Grev
Posted by: mikaelgrev on April 26, 2007 at 12:00 AM
-
Throwing a global switch might be problematic when using third-party components or libraries that aren't well-behaved, unless the switch is linked to the call stack.
- Chris
Posted by: chris_e_brown on April 26, 2007 at 12:39 AM
-
@grashalm and others - can you post a quick example of the solution with the custom RepaintManager please?
Posted by: fabriziogiudici on April 26, 2007 at 03:28 AM
-
I think 'assert' wouldn't fit very well, because it is global. Maybe some log4j-like mechanism of enabling it in 'all classes under pkgx.y.z' would be more flexible, and third-party libraries, know to be ill-behaving but that still work, to be excluded from the verification...
Posted by: ronaldtm on April 26, 2007 at 04:32 AM
-
You can enable assert() at runtime for just the set of packages that you need...
Posted by: damonhd on April 26, 2007 at 07:05 AM
-
@fabriziogiudici
http://www.clientjava.com/blog/2004/08/20/1093059428000.html
Posted by: toyin on April 26, 2007 at 07:07 AM
-
Simple solution: The bytecode format changed in Java5 and I think also in Java6. Why don't you simply make this check apply if the software has been compiled in Java6 and up? You won't break programs "in the field" because those applications are brand new.
Posted by: cowwoc on April 26, 2007 at 07:14 AM
-
The repaint manager trick is nice but not enough... There is also another partial trick which monitors the event thread and prints a stack trace if an event takes too long to process.
Neither one of these tricks is enough because I often find the lack of Swing fault checks to be a problem. The solution would be to build a "debug PLAF" that would install many runtime checks on Swing components and its own event thread/repaint manager (to detect the problems mentioned above). This would allow debugging using said PLAF yet running with the standard PLAF (performance) in deployment.
I blogged about this a while back.
Posted by: vprise on April 26, 2007 at 09:10 AM
-
Very interesting comments... On Alexander Potochkin's Blog he posted on this same subject over a year ago.
Posted by: robogeek on April 26, 2007 at 03:15 PM
-
Shai - since the comments to your entry are closed, i'll comment here. Your proposal is for a LAF to track the various violations (such as painting, change of state, graphics changes and such). Unfortunately, this is not the best solution, since:
UI delegates are easily cut off when you override paintComponent and don't call super implementation
UI delegates are don't paint the borders at all
Not everything results in repaint
The second can be addressed by replacing all borders by the delegate borders that make some checks and delegate the painting to the original borders - which can subsequently result in broken application logic (that assumes specific borders). The first and last are much harder to address.
Posted by: kirillcool on April 26, 2007 at 10:15 PM
-
Kirill - My proposal is that one specific "Debug LAF" would exist that would do that (no change to existing PLAF's)... It won't fix custom components that do all their own painting but it would improve on them since it would install the thread checks in a repaint manager of its own and a new event handler that will indicate when an event takes too long.
If a components paint feature is overridden then and paint isn't called this isn't a big deal. That makes it a completely custom component.
Like you said a PLAF can install a border on a component that makes all these checks I think that if you rely on a specific PLAF border for your logic thats probably a bug that should fail. This is designed for "proper" Swing applications that maintain the PLAF functionality and not for hardcoded Swing applications that rely on a specific PLAF.
PLAF isn't just about paint, its about a mechanism that goes almost all through Swing and through which we can install throughout Swing debugging code that would give us great coverage... Because of its nature we can even remove this code on the fly in a production environment!
Imagine this use case, a user is running with your application and gets "weird" painting behavior, you do have custom components but the error is occurring in a Swing component. You can install a debug PLAF on his machine in runtime to printout a log for you. This was an actual problem that occurred to me in deployment which I spent days tracking down :-(
Don't get me wrong, I think installing the RepaintManager/EventThreadCheck would do 90% of the heavy lifting in such a PLAF. However, delivering them in a form of a binary PLAF would make something like this remarkably easy to install on an "unaware" application. Furthermore, using the UI delegates would allow much finer control and even better checks.
Posted by: vprise on April 26, 2007 at 10:40 PM
-
Unfortunately thread confinement cannot be enforced, it can only be adhered to by convention.
For example, it's not enough to check for drawing from another thread. Modifying non-thread-safe model objects (objects which contain state which the UI depends on) from arbitrary threads is incorrect, but cannot be detected in the general case, particularly since any user-designed class could be a model.
I'm not saying the proposed ideas are worthless... if they detect some programming errors, that's great (we use a custom repaint manager as described by others). But let's be aware that they do not fully solve the problem.
Posted by: jn on April 27, 2007 at 01:53 PM
-
@CustomRepaintManager
Developers often replace this class to do all kinds of Swing tricks so it should be left alone so it remains replaceable. The whole idea of the global static RepaintManager is a bad idea in my opinion anyway. So is the global static Event Dispatch Thread - I know it's too late but the Swing arhitecture should not have been tied to this single-threaded model. It's too restrictive. And as far as I know SWT does not have an EDT.Keith
Posted by: commanderkeith on April 28, 2007 at 10:39 PM
-
Besides that, frameworks like Swing application framework should enable much simpler confinmet of a method inside edt:
we should be required only to annotate @EDT a method and leave it to the framework to put the call inside the EDT.
Posted by: hrgdavor on May 01, 2007 at 09:40 AM
|