After spending some more time working on DrJava and fixing a few unfortunate bugs that only became apparent after our release of the new stable version on Monday, Thursday and Friday, I finally found time tonight on Friday night to work on my dynamic thread checker.
It uses annotations to specify which threads should not ever be running code in a particular class or method. The threads can be identified by regular expressions for name or group name or by their ID as produced by Thread.getId
. It doesn’t do everything I’ve planned, but I must say I got a lot done tonight: The tool is already usable, I would say. Take a look at this example file:
import edu.rice.cs.cunit.threadCheck.NotRunBy;
@NotRunBy(threadNames={"foo"})
public class ThreadCheckTest {
@NotRunBy(threadGroups={".*ai.*"})
public ThreadCheckTest() {
System.out.println("ctor");
}
@NotRunBy(threadNames={"bar"}, threadIds={1})
public void run() {
System.out.println("run");
}
@NotRunBy(threadNames={"main","fum"})
public static void main(String[] args) {
System.out.println("main");
final Thread ct = Thread.currentThread();
System.out.println("thread name = '"+ct.getName()+"'");
System.out.println("thread id = "+ct.getId());
System.out.println("thread grp = '"+ct.getThreadGroup().getName()+"'");
(new ThreadCheckTest()).run();
}
}
The @NotRunBy
annotation appears four times, once in front of the class, and three times in front of individual methods or constructors. If it appears in front of the class, then it applies to all its methods, i.e. instead of writing @NotRunBy(threadNames={"foo"})
in front of the class, I could have listed "foo"
in the threadNames
array of the annotations in front of the methods.
The annotations allow you to specify an arbitrary number of thread names, group names or IDs, and as soon as the current thread entering into a method matches one of them, a violation is printed into a log file. If I run the test above, I get the following violations since the thread name is “main”, its ID is 1 and its group also is “main”:
Thread Name Violation: Current thread 'main', id 1, group 'main' Natched disallowed pattern 'main' at ThreadCheckTest.main (ThreadCheckTest.java:-1) Thread Group Name Violation: Current thread 'main', id 1, group 'main' Matched disallowed group name pattern '.*ai.*' at ThreadCheckTest.(ThreadCheckTest.java:-1) at ThreadCheckTest.main (ThreadCheckTest.java:31) Thread Id Violation: Current thread 'main', id 1, group 'main' Matched disallowed id 1 at ThreadCheckTest.run (ThreadCheckTest.java:-1) at ThreadCheckTest.main (ThreadCheckTest.java:31)
Here’s what I would still like it to do:
- I think you would want to be able to specify an
@NotRunBy
annotation in a superclass and make it apply to all subclasses too, or specify it for a method in a superclass, and then have it apply to all overriding/implementing methods in subclasses as well. This would give the programmer very strong tools to keep threads out of major portions of the code without having to modify every file. Right now, I only look at a single method and its enclosing class. - At the same time, it seems like if you have an
@NotRunBy
annotation somewhere in a subclass, you may want to hoist it into the superclass because the subclass has higher demands than the superclass, so it cannot really be treated as equal to other subclasses if we are programming at the highest level of abstraction. So I might want to also have a way for annotations to percolate up into their superclasses. That would also allow us to cover a lot of code quickly. - So far, everything is either based on names, which are often not used at all, and that’s one problem. The other problem is that anyone can assume a name. So I think I could also do a stack analysis and prevent certain classes or methods to even appear as distant callers. This wouldn’t necessarily be very much focused on the thread anymore, but perhaps we know where a thread that we want to keep out originated.
There is still a lot to do. Tomorrow I’ll make a few smaller examples, also with GUI, to demonstrate that my tool can easily find these bugs. Then I might take a look at the annotations in superclasses.
If Corky and the others like this, I will probably soon need a large codebase of multithreaded code to work with… Identify thread, and give them good names… Find classes were threads need to be kept out, write annotation… Run… I would like to use DrJava as such case study because of the double benefit of my work: I work with my thread checker, actually apply it, improve it, get it out there. And I work on DrJava, make it more robust, better commented, and perhaps I’ll even fix a few bugs.
Now it’s time to snuggle up and apologize to my girlfried that I’ve left her alone in for over 4 hours.