Trephine worker threads made easy

From Trephine

Jump to: navigation, search
« Pitfalls of multithreaded browser development Site improvements - CSS sprites »

[subscribe] Recent blog entries

Live Demos

Trephine worker threads made easy

Yesterday's article explained some of the pitfalls of multithreaded browser development, and demonstrated how to avoid them using anonymous worker threads. This article leverages the flexibility of the JavaScript language to make these operations simple and painless.

As a quick refresher, recall that in JavaScript functions are first class citizens and that objects are created from function prototypes which can be modified at any time. Combining these ideas, we find that it's possible to give functions new methods by modifying the Function prototype.

As a trivial example of this concept, consider if we gave all functions a delay() method which calls the function after a specified duration:

Function.prototype.delay = function delay( duration /*, arg1, arg2, ... */ ) {
  var self = this, args = [];
  for (var i=1, l=arguments.length; i<l; i++) args[i-1] = arguments[i];
  return setTimeout( function(){
    self.apply( null, args );
  }, duration );
};

We could then call a function in a delayed manner such as this:

var timer = alert.delay( 1000, "You're a slacker McFly!" );

As you can imagine, the above code causes window.alert to be called 1 second in the future, passing in the provided input. The return value of the function is an integer handle representing the timeout. This value is useful if for any reason you'd want to prevent the timeout from executing - ie clearTimeout(timer). Any good JavaScript library (Mootools, jQuery, etc) will probably include a delay() function or its equivalent, so chances are you won't need to write one, but it serves as a demonstration of the concept.

Now lets add a run() method to the Function prototype so that functions can be easily used to spawn anonymous threads. Consider this JavaScript which applies to the trephine/Rhino context:

Function.prototype.run = function run() {
  var self = this, args = [];
  for (var i=0, l=arguments.length; i<l; i++) args[i] = arguments[i];
  var thread = new java.lang.Thread(
    new java.lang.Runnable({
      run: function() {
        self.apply(null, args);
      }
    })
  );
  thread.start();
  return thread;
};

This is very similar to the delay example above, except instead of using a timeout to schedule code to be run, instead it creates an instance of java.lang.Thread and immediately calls its start() method. Paralleling the delay example, the return value of run() is a handle on the worker thread. This handle could be used to stop the thread or get other information as necessary.

Here's an example showing how to use run():

function sleepMessage( message, duration ) {
  var thread = java.lang.Thread.currentThread();
  thread.sleep( duration );
  java.lang.System.out.println( message );
}
var sleepThread = sleepMessage.run( "What are you McFly, chicken?", 1000 );

When we call sleepMessage.run, the arguments are collected and passed back into sleepMessage inside the Thread. Thus in line 2, java.lang.Tread.currentThread() returns a handle to the newly created child thread. As you can see, the behavior of sleepMessage is pretty analogous to the previous example using alert.delay.

This may seem a little complicated, but it's certainly less coding than writing out all that Thread subclassing by hand each time. Also, we can trim it down by using an anonymou function rather than a named function. Here's the same example, but without naming all the various worker functions and variables:

(function( message, duration ) {
  java.lang.Thread.currentThread().sleep( duration );
  java.lang.System.out.println( message );
}).run( "What are you McFly, chicken?", 1000 );

Whew, I hope all that made sense. Event-driven development its tricky enough to get right, even without introducing the complexity of threaded management. That said, I believe that the forthcoming wave of ultra-rich Internet applications will demand resources in excess of what can be accomplished using traditional browser capabilities, and so understanding when and how to employ threaded techniques will be a crucial differentiator between the masters and the masses.

Note that the snippets and examples in this article target the Rhino/Java environment, and so need to be called through trephine.js to be effective. In an upcoming article, I'll discuss making similar changes to the browser JavaScript environment so that worker threads are easy to create everywhere.

As always, I look forward to your questions, suggestions and comments!

Public domain declaration

Just so there's no confusion: all of the code snippets on this page are provided "AS IS", without warranty of any kind, express or implied.

All of the code snippets on this page are hereby released into the public domain by the me, the copyright holder. This applies worldwide. Or in case this is not legally possible: The copyright holder grants any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law.

If you'd feel better with a "real" license, you're free to use code snippets on this page under the MIT license as described on the about page.

Any links back to this site are always appreciated, but not required. Enjoy!

--Jim R. Wilson (jimbojw) 15:57, 22 April 2009 (UTC)