Sunday, September 13, 2009

Web Worker API Shim Demo Posted

I finally got the Web Worker API shim demo posted on Google Code tonight.

There is an interesting trick to posting the demo HTML page where you need to set the svn:mime-type property to "text/html" or it won't be served as HTML. Makes sense but confused me for a while and made me sidetrack looking at cleaning up my old portfolio site.

Not that that wouldn't be a great idea, too.

Web Worker Demo

To see the shim, visit that URL with a non-supporting browser that has Google Gears installed. You may also be interested in comparing it against browser implementing web workers natively such as FF 3.5.

The shim is implemented via two classes. Probably the simplest way to understand how they work is from the inside out. Implementing the Worker object and the worker-to-worker-thread messaging mechanism on top of Gears is pretty simple. But the API doesn't just cover instantiating a worker, giving it some work to do and talking to it. The API defines a window-like environment that the worker thread operates in, where other APIs are available:

WorkerGlobalScope

Once the WorkerGlobalScope is created, the Worker itself is a dispatcher, sending and receiving messages. So what does the WorkerGlobalScope look like and how do we make one?

The HTML5 Web Workers API spec covers the WorkerGlobalScope in detail. WorkerGlobalScope.js is my implementation. In it, we have constructor functions for each of the WorkerGlobalScopes (Dedicated, Shared and the "base") which handle setting instance variables both private and public. We also have DedicatedWorkerGlobalScope onmessage prototype and a prototype object which handles all of the methods available in any WGS, including empty Worker and SharedWorker constructor methods.

But the WorkerGlobalScope established by the constructor and prototype is incomplete -- not only because I haven't finished everything! It is missing the very crucial piece of providing a working Worker implementation.

Of course, we can't have a WorkerGlobalScope without having already created a Worker. The implementation of Worker is provided not directly by WorkerGlobalScope but by the calling Worker. The DedicatedWorker.js file bootstraps the Worker into the originating window environment with the InitWorker method that returns the DedicatedWorker constructor.

Simplified:

Worker = (function InitWorker(window) {
function DedicatedWorker(url) {
...
}
return DedicatedWorker;
})(this,navigator,WorkerGlobalScopeSource);


The InitiWorker method provides closure around the bootstrap parameters. "this", the global scope for workers. "navigator", the navigator object passed through from the global scope. The source which can be used to create the WGS. Because it is named, it can be easily passed as a string via function decompilation into the Gears worker pool thread. It is my understanding that because it is defined as part of an assignment operation, it does not pollute the namespace (I think there are some finer points there, but I don't know them as well as I would like).

When we create the code to execute our worker's payload in the workerPool, which must instantiate the worker global scope and make all of the APIs available to the worker, we basically repeat the exact same construction. There it is text and not source code, but it is still the same thing. In this way, we share the source code for Workers and WorkerGlobalScope with all child instances even though we cannot directly pass the objects.

The code which is executed by the Gears workerPool thread:


this._source = [
wgsSource,
"var wgs = new DedicatedWorkerGlobalScope(\""+url+"\");",
"wgs.navigator = " + toSource.call(navigator)+";",
"wgs.navigator.online=true;",
"Worker=wgs.Worker=("+InitWorker+")(wgs,"+ toSource.call(navigator) +",'"+ escapeQuotes(wgsSource)+"');",
"wgs._loadSource({url: ['" + url + "'], "+
"callback: function(scripts){ wgs._scripts = scripts; wgs._execute('importScripts(\""+url+"\")'); } });"
].join("\n");


Matters are complicated by the fact that the HttpRequest object (analog to XHR) available in Gears does not allow for synchronous operations but that is a story for another day.

As the credits on the demo say, thanks to Andrea Giammarchi for pointing me at the need for this shim.

No comments:

Post a Comment