Posts tagged “blogroll”

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)

Faceball 2007/08/01

The speculation is over -- I finally played Faceball.  Allspaw and I traded wins yesterday afternoon after work and I am all over the Faceball bandwagon now.  Something about getting continually hit in the face just gets you in the right frame of mind to throw things back at the other guy.  The game really motivates itself and yet has just enough rules to be civilized.  And to top it off, it appeals to my childhood obsession with throwing things indoors.

Comments (1)

Is there honor left in bike racing? 2007/07/26

I just began watching Stage 17 of this year's Tour de France, recorded from this morning, and was greeted with a second punch in the face from the collective cycling elite. Only two days after Alexandre Vinokourov failed a drug test, the Danish team Rabobank has released their star climber and current race leader Michael Rasmussen. He has not failed a drug test but his sneaking around Italy and avoiding team-mandated off-season drug tests has cast a shadow on his integrity.

It may seem odd to dismiss this rider before he officially failed a test but I agree with former professional cyclist and commentator Paul Sherwen that drastic measures are required. I wanted badly to believe that last year's shakedown among the peloton's elite riders was the last straw but I've been betrayed once again. This year has only proven that no tolerance for doping is not enough. Professional cycling cannot afford to tolerate even suspicion anymore.

What is next for this once-great sport? Every accomplishment is now doubted. Every duel is now unequal. Every fan watching now wonders whether what they're seeing is possible. This is not sustainable. In the name of everyone like me who wishes he could ride like the pros, fix this. Vive le tour.

Comments (0)

Develop and deploy on the same box with Capistrano 2007/07/22

You're crazy if you're not deploying Rails apps with Capistrano. This tool makes it dead-simple to push your latest Subversion to your production server, run database migrations and restart your server all in one simple command. Plus with extra tasks like cold_deploy, getting a new box added to the pool is a snap. One problem, though — this tool was designed with real environments in mind, and my one-box setup for development and production doesn't quite fit in. If you're in the same boat, fear not, I have a solution.

The root of this problem is the assumption that your development and production server environments are identical. But when deploying to localhost, using the same mongrel_cluster.yml is just not an option, since you need to use separate ports for your development and production versions.

We need to user two different mongrel_cluster.yml files, one for production and one for development. We can leave the development version out of Subversion and create the production version over again on each deploy. But how do we deal with actually restarting the server?

It turns out Capistrano creates a directory structure that supports this directly, since the *.pid files in the log/ directory are symlinked into place and persist between deployments. So it seems like we can just create the config file and restart the server, right?

Wrong. deploy.rb's run command doesn't quite work like your average, everyday shell script. To help it out, I created a shell script that handles the server restart. The shell script allows me to change directories into the application directory and set the PATH variable so mongrel_rails can be found.

Putting it all together, here is config/deploy.rb:

set :application, 'halvesies'
set :repository, 'protocol://repository/url'

role :web, 'localhost' role :app, 'localhost' role :db, 'localhost'

set :deploy_to, '/path/to/production/app'

task :restart, :roles => [:web, :app, :db] do run "#{deploy_to}/current/config/restart.sh" end

And here is config/restart.sh:
#!/bin/sh
cd /path/to/production/app/current
export PATH="$PATH:/var/lib/gems/1.8/bin"
mongrel_rails cluster::configure -e production -p 11001 -a 127.0.0.1 -N 3
mongrel_rails cluster::restart
cd -
And after all of this, you still deploy your site by just running cap deploy.

Comments (1)

CVS oops fix 2007/07/12

Committing a new project to CVS is quite a chore. I have been slowing getting it together for the past couple of days, screwing up early and often. If you ever find yourself with a directory tree full of CVS directories and you would rather not, try this:

<?php

function scrub($path) { $dir = opendir($path); while (false !== $d = readdir($dir)) { if ('.' == $d{0}) continue; if ('CVS' == $d) `rm -rf $path/$d`; else if (is_dir("$path/$d")) scrub("$path/$d"); } } scrub('.');

?>

I've discovered other nasty quirks, too. Like you can't actually remove a directory, but you can suppress empty ones on cvs update using the -P option. Adding binary files and trusting CVS to play nicely is a terrible idea. Instead you need to think for it and do cvs add -k b file to let it know you're adding a binary file.

Or, if you have decision-making power, you could just switch to Subversion.

Comments (0)

Comcast DVR != TiVo 2007/07/09

Disclaimer: I am not planning on buying a TiVo Series 3 box.

I am now officially a discouraged Comcast HD DVR customer. This service and the hardware that provides it is absolutely terrible. I will cover the various deficiencies in its user interface in a moment, but today while watching the 3-hour recording of Stage 2 of the 2007 Tour de France (which was quite a stage, let me tell you) the sound would intermittently and inexplicably cut out. The silence would, as far as my patience let me go, continue indefinitely, but was always fixed by hitting the quick-back button (or certainly just by interrupting the recording in any way). How can playing back a recording be so difficult for a box Motorola built for this exact purpose?

So far this is the only functional issue I have had with the machine but that is not to say that it is without other flaws. The rest of this post will be an attempt to justify the $800 price tag of a TiVo Series 3. I of course have already spoiled the conclusion, but bear with me.

Having been spoiled for the past two years with a TiVo Series 2, I know that folders are a very convenient way to organize recorded programs. Motorola missed this memo and instead provide a flat list of recordings, ordered by date, channel or title. Combine this with the fact that the interface only fills the lower half of the screen and you get a very trying experience.

Not that keeping a long list of recordings is terribly easy. I have not yet decided if there are software bugs or if there are hidden limitations on the Season Pass equivalent but in either case, it is very difficult to setup repeated recordings of your favorite shows. When reordering series to record, no warning is given when conflicts (even with the dual-tuner) will alter the upcoming recording schedule. When setting up a series, it is not clear whether all channels are searched for upcoming titles.

And of course this uncertainty has other consequences. Whenever I browse through the program guide, I expect some indication of which programs are scheduled to be recorded. Sometimes there is. Other times there is not. And even two levels deep in menus I am sometimes unsure whether a program is set to record.

All of this, though, and I cannot justify $800. A damn shame.

Comments (1)