Crowley Code! 
 (Take 12)

Threading in Gecko 1.9 2007/08/07

Things change quickly in Internet-land, and between versions 1.8 and 1.9, the Gecko SDK received a major upgrade to its thread library. What once took multiple XPCOM objects, more than few hacks and a prayer for good measure can now be done in a single Javascript file with minimal interference from the world of C++ beneath.

Here is a sample of how one might spin an event off onto a background thread acting as a job queue. The execution is fairly straightforward: a background thread is created; the UI dispatches events onto this background thread; the job on the thread dispatches an event back to the UI thread when it is finished.

Before we start, let's define the objects used to cross the thread boundary. They must be XPCOM objects (but can be implemented in pure Javascript and don't even need to live in the components/ directory) and must implement the nsIRunnable interface. This interface requires that QueryInterface respond positively to nsIRunnable and that a run() function be defined.

var Foo = function(id) {
	this.id = id;
};
Foo.prototype = {
	run: function() {
		try {

			// Do some work, for now just print something
			Components.utils.reportError('This is where the background work happens.');

			// Callback to the main thread
			main.dispatch(new FooCallback(this.id, 'SUCCESS'),
				background.DISPATCH_NORMAL);

		} catch (err) {
			Components.utils.reportError(err);
		}
	},
	QueryInterface: function(iid) {
		if (iid.equals(Ci.nsIRunnable) || iid.equals(Ci.nsISupports)) {
			return this;
		}
		throw Components.results.NS_ERROR_NO_INTERFACE;
	}
};
var FooCallback = function(id, result) {
	this.id = id;
	this.result = result;
};
FooCallback.prototype = {
	run: function() {
		try {

			// This is where we can work with the main thread after the job
			alert('id: ' + id + ', result: ' + result);

		} catch (err) {
			Components.utils.reportError(err);
		}
	},
	QueryInterface: function(iid) {
		if (iid.equals(Ci.nsIRunnable) || iid.equals(Ci.nsISupports)) {
			return this;
		}
		throw Components.results.NS_ERROR_NO_INTERFACE;
	}
};

Here is the code we need to actually use these objects to spawn a background thread and dispatch a job to it. The callback will end up on the main thread's event queue.

background = Cc['@mozilla.org/thread-manager;1'].getService().newThread(0);
main = Cc['@mozilla.org/thread-manager;1'].getService().mainThread;
background.dispatch(new Foo(id), background.DISPATCH_NORMAL);

The first two lines here should be executed on application startup and will leave thread references in main and background. The third line actually puts a job on the background queue. This can be called anywhere and will likely be used inside a loop to push a lot of compute-intensive work onto the background thread, allowing the UI thread to remain responsive.

A note about the id passed to the thread objects: this is not strictly necessary, but I find it very convenient to have the option of tracking individual jobs through the event queue. The id is especially useful when you're using the callback to update a certain element of the UI with the result of the job. Then in the callback it's as simple as document.getElementById(this.id).

Apologies for the bits-and-pieces examples. I'm ripping this out of unreleased code so I don't have time to create a demo app for each one. I recommend starting with Mark Finkle's hello world.

Comments (119)

  1. Thanks a lot,

    that really helped a lot.

    Martin

    Martin S. — 2007/09/24 6:13 pm

  2. thanks richard for this input.

    i have used your code and wrapped it into classes, and everything was working fine with the 1.9b2.

    since then, same code crashes. i've filed a bug to bugzilla here: https://bugzilla.mozilla.org/show_bug.cgi?id=413671 in which i think you might be interested.

    one of the commenters states that 'you may not use xul windows to create javascript objects which you then pass to other threads'.

    ..any clues or comments appreciated :)

    cheers,

    r.

    — Roberto Ostinelli — 2008/02/11 3:23 am

  3. hi,

    Need to know that it will not work with lower version of Gecko or not?

    I am working with Gecko 1.8.1.3_0000000000

    My application is desktop application. I am using xul for UI and I need to use threading in that (javascript)

    I tried the code and it will giving error for creating instance of thread manager itself. ( might be because its in firefox 3)

    So If you know any solution for that, then pls let me know.

    Thanks

    — Krishna — 2008/03/11 1:46 am

  4. Hello,

    thank you for explaining. I followed the steps and it works. But when I put an expression like

    var i=new Date().getTime();

    in the background thread, Firefox 3 beta 5 freezes. It also happens with loop. What might be the reason for that? Even I can not find a sleep method in the new Thread API. How can one let a thread sleep for a while in Gecko 1.9 ?

    Best regards ninjafette

    — ninjafette — 2008/04/26 9:55 am

  5. Good point, ninjaffette. I'm also looking for a Thread.sleep function.. does not seem to be there. Very annoying..

    — Arjan — 2008/06/21 7:47 am

Richard Crowley?  Kentuckian engineer who cooks and eats in between bicycling and beering.

I blog mostly about programming and databases.  Browse by month or tag.

To blame for...


© 2009 Richard Crowley.  Managed by Bashpress.