Wednesday, October 25, 2006

JavaScript in IE is not Multithreaded

After coding up my mutex and experimenting with it, I've discovered that Internet Explorer isn't multi-threaded. Instead, it has something worse. Much, much worse.

In any case, here's my mutex code:


function Mutex(maxThreads)
{
// This mutex is based on the paper "Tight Bounds for Shared Memory Symmetric
// Mutual Exclusion Problems" by Eugene Styer and Gary L. Peterson.
if (maxThreads < 2)
alert('Mutex is configured incorrectly');
this.waiting = new Array(maxThreads);
this.locks = new Array(maxThreads);
for (var n = 0; n < maxThreads; n++)
{
this.waiting[n] = null;
this.locks[n] = null;
}
this.maxThreads = maxThreads;
this.whoInCriticalSection = null;
this.turn = null;
this.get_visible = function(level, retry, me) { // This is private
if (level == -1)
{
this.waiting[0] = me;
level = 0;
retry = true;
}
else if (this.waiting[level] != me)
{
// Don't write if we can make progress
if (this.turn != me)
{
if (retry)
{
// Assume mistake, once
retry = false;
}
else
{
level = level + 1;
retry = true;
}
// Make visible again
this.waiting[level] = me;
}
}
return [level, retry];
}
this.enter = function() {
var me = new Object(); // generate a unique id for this thread
var level = -1;
var retry = false;
while (true)
{
// See if lock is available, or if it assigned to me
while (this.turn != null && this.turn != me)
{
var ret = this.get_visible(level, retry, me);
level = ret[0];
retry = ret[1];
}
this.turn = me;
var locked;
while (true)
{
// Try to get all locks
for (var pos = 0; pos < this.maxThreads; pos++)
{
if (this.locks[pos] == null)
this.locks[pos] = me;
}
// Do we have all the locks?
locked = true;
for (var pos = 0; pos < this.maxThreads; pos++)
{
if (this.locks[pos] != me)
locked = false;
}
if (this.turn != me locked) break;
}
if (this.turn != me)
{
// Lost, release locks
for (var pos = 0; pos < this.maxThreads; pos++)
{
if (this.locks[pos] == me)
this.locks[pos] = null;
}
var ret = this.get_visible(level, retry, me);
level = ret[0];
retry = ret[1];
}
else
{
break;
}
}
// Acquired lock
if (this.level > -1 && this.waiting[this.level] == me)
this.waiting[this.level] = null;
this.whoInCriticalSection = me; // Save our id for later
}
this.leave = function() {
var me = this.whoInCriticalSection; // Get our saved id
this.whoInCriticalSection = null;
// Find ID of top waitier or 0 if nobody
var next = null;
for (var pos = this.maxThreads - 1; pos >= 0; pos--)
{
if (this.waiting[pos] != null) {
alert('handoff');
next = this.waiting[pos];
break;
}
}
// Let them continue, release locks
this.turn = next;
for (var pos = 0; pos < this.maxThreads; pos++)
{
if (this.locks[pos] == me)
this.locks[pos] = null;
}
}
}
So back to the original point. In the end, JavaScript in Internet Explorer isn't multi-threaded. There's only one thread, but it can be interrupted by callbacks or signals, and the thread won't regain control until after the signal/callback handler completes.

So if you look at this chunk of JavaScript code
var image = new Image();
image.onload = function() {
alert('a0');
alert('a1');
};
image.src = 'blank.gif';
alert('0');
alert('1');

you'll notice that the alerts you get are for a0, a1, 0, and then 1. The onload callback does not give up control back to the main thread until after it finishes giving its two alerts. This means that mutexes and critical sections can't be used in JavaScript because if one thread is inside a critical section and becomes interrupted, the other thread cannot yield to the other thread to let it exit its critical section. So basically, you can't use locks or critical sections or other standard programming techniques to protect important parts of code.

I don't know what the best way to fix this is. It's just messy...very, very messy.

No comments:

Post a Comment