This might be a bit unusual. I have a fairly reasonable proposal for supporting web workers in GWT proper, and a working proof of concept that shouldn't come anywhere near the core toolkit. Everyone seems busy with what looks like 2.1 and the upcoming I/O, so I'm not expecting miracles, but I would appreciate any wider perspective and maybe some Compiler insight to make the implementation less of a complete hack.
A few months ago I put together a module to get workers working nicely with the GWT compiling/linking process. It was originally just on a bit of a lark, but it's proven so easy to use (thanks to GWT, not me) that I've ended up developing a few projects with it, two examples of which I've posted over with the code (http://code.google.com/p/gwt- ns/). I finally have some time to come back to the module and finish some lingering issues, but thought I might try and get some other perspectives first. If there's a GWT 2.1 web worker skunkworks project already in progress, please let me know now =) To give credit where its due: several files came from the Speedtracer worker overlay code and one or two files came from GALGWT. I've preserved copyright notices everywhere, and everything else is also Apache 2.0 licensed. I also finally got around to watching 2008's GWT Extreme! and found out my compilation scheme is very similar to what Ray Cromwell was up to two years ago (which probably happens a lot). He called it the Generator+Linker+Generator+Linker pattern, though in this case it's more Generator+Linker(+Generator+Linker)*. Anyway, like Ray Ryan, I wish this were a wave, but I'll stick to the board for now. I tried to keep it short, but it got rather long, so if there is interest I can make it look a bit more like an actual design document in a wave and hook it up to the water cooler. === Motivation === Web workers, as currently specified http://www.whatwg.org/specs/web-workers/current-work/ have two primary requirements. They are separate Javascript files, loaded in a worker constructor that 1. Doesn't access the DOM 2. Don't share state or context with other execution contexts (e.g. their parent script). Once a worker is loaded and running, the worker and its parent can pass messages to each other, usually in JSON form. At least for transitional worker support, I'm suggesting a third condition 3. No single code execution will throw a slow script warning, even when run *normally* in an older/slower browser. This does necessarily limit functionality, e.g. ruling out the usual worker examples of blocking I/O and long running calculations. The first isn't really welcome in the GWT world, anyways, and really any synchronous operation is a bad fit for GWT/Javascript. This condition can also just be considered transitional; I'll go with it for now and just assert that another level of deferred binding could give allow more flexibility on this point. Accepting these three conditions, a true worker object becomes functionally equivalent to a simple isolated object with only post and receive message methods in its exposed interface (an Actor, if you're into that sort of thing). In practice, the only difference is that some newer platforms will execute the native worker off the main thread. In exchange for some loss of flexibility, instead of compiling the workers separately, permutations for non-worker-supporting browsers can load the worker code into an asynchronous wrapper (I'm calling it a proxy, but I'm sure there's a more descriptive pattern name out there) and run it on the main thread. This gets you -- a single code base, regardless of browser worker support -- Full development mode support, for the parent and worker scripts, without any OOPHM alterations. The generator just loads the non-worker version of the worker object, regardless of browser. === Goals === -- Enable the compilation of a GWT worker module to a valid worker script (per current specification). -- Allow any GWT module to instantiate worker modules as a worker object, regardless of target platform's support for native javascript workers -- This includes allowing workers to themselves instantiate sub- workers, including from their own module (per spec). -- Absolutely minimal overhead to ensure gain from use of workers -- Ensure the same behavior from emulated workers as from native workers -- Enable normal development-mode debugging of workers and parent program as they execute. -- Simplest possible construction of worker objects. If a worker module is on the classpath, the module name is enough information to create the worker object, since no state or context will be shared except what is explicitly passed in a message string. -- Creating a native worker should result in only a single server request. -- Worker script files should be aggressively cacheable. -- Adding workers to a project should be as transparent as adding any other module to the build process (excepting logged information and extended compilation time) -- Adding workers should not alter the behavior of any other Linkers defined in a project (most importantly the std, sso, and xs primary linkers) === Potential Future Goals === -- Different entry points for emulated and native workers. -- The option to have the generator compile a worker module (or specify an already compiled script) for development mode to debug behavior (but not Java source) of native worker. -- Built-in IPC to allow GWT compiler to better do its magic === Current Implementation === **Implementation goals** -- As stated, no change for other linkers. -- To patching of GWT. (this necessitated some workarounds) **Valid Worker Modules** Implicit requirements to compile a module as a worker (currently unchecked) -- No DOM access -- No shared state or context with other execution contexts. -- No SSW-generating code Explicit requirements to compile a module as a worker (currently enforced) -- Addition of a primary linker which packages the resulting script in a simple bootstrapping closure -- A single specified entry point which extends WorkerEntryPoint and implements the required methods -- Properties set such that only one permutation is produced -- No code splitting. The last three aren't set in stone. For instance, multiple permutations might be needed for i18n reasons. Support is certainly possible, but makes the script selection process more complicated than the one described below. By contrast, code splitting seems a little strange in a worker context, but importScripts() is practically built in support for it. **To Use a Worker Module** Currently, the canonical name of a valid Worker module is all the information needed. Example usage: @WorkerModuleDef("pkg1.pkg2.ModuleName") interface MyWorkerFactory extends WorkerFactory { } MyWorkerFactory factory = GWT.create(MyWorkerFactory.class); ... Worker myWorker = factory.createAndStart(); An already running worker object is returned. Messages can be passed to the worker via postMessage(), handlers can be attached to listen for messages and errors from the worker, and the worker can be stopped at any time by calling terminate(). **Non-native Implementation** For platforms without worker support, the Generator creates a new instance of the module's specified entry point and wraps it (as described above). The wrapper is returned. Messages passed into and coming out of the wrapper are queued to decouple worker and parent execution contexts. **Development Mode/Debugging** Worker emulation that is "close enough" to a native implementation can be used in its place. This means that the main script and all workers are run in (fast) java-land and can be debugged there. This is currently accomplished by development mode always triggering emulated support, regardless of the browser used. **Native Implementation** This is where the build process becomes a little unusual. The general overview: the generated factory uses a native method to return a Javascript worker object, created from a URL that points to where the worker module's compiled script will be. All worker functionality is handled by native methods. The Generator also emits a WorkerRequestArtifact with a reference to the indicated module. The associated (pre) Linker collects all requests and runs another GWT Compiler in a second process to compile the worker modules, then inserts the finished scripts into the correct directory. This last point causes some issues, mostly because workers can arbitrarily create more workers. 1. Strong naming: To allow caching, worker scripts must be strongly named. But because workers can spawn workers within themselves by loading from a url, a change to one worker, which causes a change to its strong name, will in turn cause a change in the strong name of its parent. Because the worker creation graph could potentially be fully connected, I just took a page out of code splitting and workers are found at /workerjs/strong_name_over_all_workers/modulename.cache.js. Conveniently, since workers are loaded by relative url, the strong name isn't needed by them, just the module name. However, the parent module does need the full strong name, and--unlike code splitting-- because all permutations (currently) share a set of workers, the name is unrelated to any particular permutation's strongname. It is admittedly a complete hack, but I currently insert a placeholder string in the Generator stage, then do search and replace in the compilation result in the pre-linker, after the worker scripts have been compiled and linked. A far better solution would be something like how the strongname of a permutation is included within the emitted script, but without altering the primary linker or selection scripts I'm not sure how this could be accomplished 2. A new process and compilation every time a worker is needed isn't acceptable when workers can create workers can create workers.... Since workers only need to know the module name, when a module detects that it is being compiled in the secondary level, it just sends a message back up to the primary level to enqueue the request for the worker script it needs, then just loads from the relative url (no strong name needed) and relies on the parent compilation process to create the script. However, even with improvements to these two points, fundamentally this process is very redundant. Creating a worker is actually a somewhat simpler form of code splitting, except instead of shared code all going in the first loaded fragment, it needs to be part of every fragment that uses it. In this implementation, because older-browser permutations load the worker code directly, full ASTs are being generated, used for some permutations, discarded, and then created again in a second process. Ridiculous, but it was the most efficient way I could find to do the job without patching the core library. I also didn't want to get into creating a code-splitting-like feature that doesn't interfere with actual code splitting, not to mention linkers, selection scripts, etc etc, which this approach is able to side step. === === Like I said, the code is still early and many features are currently unimplemented. Notably, I've only got it working on the simplest build system possible (Eclipse plugin on a single computer), so I'm not sure what will happen in more complicated setups (custom ant scripts, maven, etc). If that wasn't enough, I've written a bit more here http://extremelysatisfactorytotalitarianism.com/blog/?p=645 http://extremelysatisfactorytotalitarianism.com/blog/?p=932 The first goes into how one of the samples works. The second makes the argument that, if you're already using the MVP pattern or something like an event bus, the transition to using workers may tip heavily to the benefit side of a cost/benefit analysis. The examples I've released publicly are mostly just toys, but hopefully I can talk about something more enterprise-y soon. For me, I've found workers to be incredibly easy to use and integrate into my own projects, far easier than I expected. This is partly due to the dead-simple worker spec, but most of it has to do with the completely amazing toolset you guys have created and continue to improve. Magic from good engineering. I'm sure you'll be hearing that sort of thing a lot more in a month or so, but I thought I'd get it in early. Again, I'd appreciate any thoughts in this busy time. If I'm missing something incredibly obvious, let me down easy. -- http://groups.google.com/group/Google-Web-Toolkit-Contributors To unsubscribe, reply using "remove me" as the subject.