I made a few small improvements to the dynamic thread checker tonight, after fixing another bug in DrJava’s latest stable release. Since we’re going to release another stable release soon, perhaps today, I wonder if we can demote the current stable to beta 4.
I wasn’t feeling too well on Saturday and Sunday, so unfortunately I didn’t get quite as much done as I had wanted. I nonetheless made two important additions… well, maybe one, but it seems like they’re two.
First of all, I added a dual @OnlyRunBy
annotation that designates the only threads that may run the annotated code. @NotRunBy
designates threads that may not run it. Implementing @OnlyRunBy
was a lot harder than @NotRunBy
, unfortunately. The latter fails immediately when a thread is found that matches one of the designated threads; @OnlyRunBy
, however, only fails when it has not matched with any of the threads specified. That means I need to do some bookkeeping. I tried a few different ways, and then finally decided to add lists to the ThreadCheck
class that first get filled and then processed in a single call.
The second addition is special treatment for the event thread by using an eventThread=true
element in the annotation. I’ve only implemented this for @OnlyRunBy
, though. That’s where it makes the most sense to me. Because @OnlyRunBy
didn’t exist before today, maybe all of this was just one major addition, but eventThread=true
seems pretty important in its own right.
Here is a sample program that creates a window with a button:
import edu.rice.cs.cunit.threadCheck.OnlyRunBy;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
public class ThreadCheckTest2 {
public void run() {
JFrame frame = new JFrame("ThreadCheckTest2") {
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (WindowEvent.WINDOW_CLOSING == e.getID()) {
System.exit(0);
}
}
};
JPanel panel = new JPanel();
JButton button = new JButton("Button");
button.addActionListener(new ActionListener() {
@OnlyRunBy(threadNames={"AWT-EventQueue-.*"})
public void actionPerformed(ActionEvent e) {
handleButton();
}
});
panel.add(button);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
handleButton();
}
@OnlyRunBy(eventThread = true)
private void handleButton() {
System.out.println("Button was pushed");
}
public static void main(String[] args) {
(new ThreadCheckTest2()).run();
}
}
Note that handleButton
is called from the end of the run
method even though handleButton
has been annotated with @OnlyRunBy(eventThread = true)
. This generates the warning below:
Thread Violation: OnlyRunBy Current thread 'main', id 1, group 'main' did not match the event thread at ThreadCheckTest2.handleButton (ThreadCheckTest2.java:-1) at ThreadCheckTest2.run (ThreadCheckTest2.java:34) at ThreadCheckTest2.main (ThreadCheckTest2.java:51)
Farther up, annotating the ActionListener.actionPerformed
method in the second anonymous class is another annotation @OnlyRunBy(threadNames={"AWT-EventQueue-.*"})
that pretty much does the same, except using the thread name, which is a little less reliable.
I haven’t dealt with the superclass/same method in superclass issue because I want to talk to Corky and the other students first and see what their intuition about this behavior is. I also started writing a version of the thread checker for both annotations purely using reflection, but I couldn’t figure out how to uniquely identify the method that was entered just using the data from a StackTraceElement
. I get the class and method name, but their might be more than one method with that name. I guess I could statically make that determination in the instrumentor and then simply pass a parameter to the method. I’m just interested to see how well reflection performs compared to the code I have right now: The reflection code may be shorter, but I doubt it can be jitted as well.
I’ll see if I can go to sleep now, it’s just past 4 AM. Doubtful, but we’ll see…