Tuesday, March 11, 2008

Avoiding ThreadDeath with env.js

At Appcelerator we use John Resig's "simulated browser environment for Rhino" as part of our IDE. I'd had intermittent problems with a ThreadDeath error being thrown, which would then cause any other thread running Rhino to hang.



I wasn't sure if the problem was in my Java code, my JavaScript, Rhino's Java code, or Aptana's Java code (we built our IDE atop their HTML/CSS/JS editor). Turns out it wasn't any of those! It was in that "simulated browser environment" code, in window.clearInterval where the thread spawned with setInterval is killed. I puzzled for a moment over why Mr.Resig was using multiple threads rather than a single one for setIntervals (expediency I assume), and then changed two lines so that it doesn't kill the thread, but allows it to die of natural causes.


window.setInterval = function(fn, time){
var num = timers.length;

timers[num] = new java.lang.Thread(new java.lang.Runnable({
run: function(){
while (true) {
while (timers[num]) {
java.lang.Thread.currentThread().sleep(time);
fn();
}
}
}));

timers[num].start();

return num;
};

window.clearInterval = function(num){
if ( timers[num] ) {
timers[num].stop();
delete timers[num];
}
};


Another example of a day of debugging yielding a two line fix. Ugh.

2 comments:

ynniv said...

John Resig's "simulated browser environment for Rhino" sounds like Aptana's Jaxer. Any thoughts on how the two compares?

Mark said...

Jaxer is actually running something like a headless Firefox/XULRunner/Prism internally, so it's running SpiderMonkey and has a real DOM implementation. The env.js script (usually) relies on a Java DOM implementation and has some major incompatibilities with "regular" browsers. They're also using Java XPCOM to communicate between the Mozilla bits and the Jetty bits.
I'd like to be able to run Jaxer minus the Jetty part – to run it as a command-line tool for preprocessing dynamic HTML and testing, but haven't had the time to do that yet.
(Good to hear from you Vinny!)