webworker
Version:
An implementation of the HTML5 Web Worker API
167 lines (139 loc) • 4.86 kB
JavaScript
// Launcher script for WebWorkers.
//
// Sets up context and runs a worker script. This is not intended to be
// invoked directly. Rather, it is invoked automatically when constructing a
// new Worker() object.
//
// usage: node worker.js <sock> <script>
//
// The <sock> parameter is the filesystem path to a UNIX domain socket
// that is listening for connections. The <script> parameter is the
// path to the JavaScript source to be executed as the body of the
// worker.
var assert = require('assert');
var fs = require('fs');
var net = require('net');
var path = require('path');
var sys = require('sys');
var vm = require('vm');
var wwutil = require('./webworker-util');
var WebSocket = require('./websocket-client').WebSocket;
var WebSocketServer = require('./ws').WebSocketServer;
var writeError = process.binding('stdio').writeError;
// Catch exceptions
//
// This implements the Runtime Script Errors section fo the Web Workers API
// specification at
//
// http://www.whatwg.org/specs/web-workers/current-work/#runtime-script-errors
//
// XXX: There are all sorts of pieces of the error handling spec that are not
// being done correctly. Pick a clause, any clause.
var inErrorHandler = false;
var exceptionHandler = function(e) {
if (!inErrorHandler && workerCtx.onerror) {
inErrorHandler = true;
workerCtx.onerror(e);
inErrorHandler = false;
return;
}
// Don't bother setting inErrorHandler here, as we're already delivering
// the event to the master anyway
ms.send([wwutil.MSGTYPE_ERROR, {
'message' : wwutil.getErrorMessage(e),
'filename' : wwutil.getErrorFilename(e),
'lineno' : wwutil.getErrorLine(e)
}]);
};
// Message handling function for messages from the master
var handleMessage = function(msg, fd) {
if (!wwutil.isValidMessage(msg)) {
wwutil.debug('Received invalid message: ' + sys.inspect(msg));
return;
}
switch(msg[0]) {
case wwutil.MSGTYPE_NOOP:
break;
case wwutil.MSGTYPE_CLOSE:
// Conform to the Web Workers API for termination
workerCtx.closing = true;
// Close down the event sources that we know about
ws.close();
// Request that the worker perform any application-level shutdown
if (workerCtx.onclose) {
workerCtx.onclose();
}
break;
case wwutil.MSGTYPE_USER:
// XXX: I have no idea what the event object here should really look
// like. I do know that it needs a 'data' elements, though.
if (workerCtx.onmessage) {
e = { data : msg[1] };
if (fd) {
e.fd = fd;
}
workerCtx.onmessage(e);
}
break;
default:
wwutil.debug('Received unexpected message: ' + sys.inspect(msg));
break;
}
};
if (process.argv.length < 4) {
throw new Error('usage: node worker.js <sock> <script>');
}
var sockPath = process.argv[2];
var scriptLoc = new wwutil.WorkerLocation(process.argv[3]);
// Connect to the parent process
var ws = new WebSocket('ws+unix://' + sockPath);
var ms = new wwutil.MsgStream(ws);
// Once we connect successfully, set up the rest of the world
ws.addListener('open', function() {
// When we receive a message from the master, react and possibly
// dispatch it to the worker context
ms.addListener('msg', handleMessage);
// Register for uncaught events for delivery to workerCtx.onerror
process.addListener('uncaughtException', exceptionHandler);
// Execute the worker
scriptObj.runInNewContext(workerCtx);
});
// Construt the Script object to host the worker's code
var scriptObj = undefined;
switch (scriptLoc.protocol) {
case 'file':
scriptObj = new vm.Script(
fs.readFileSync(scriptLoc.pathname),
scriptLoc.href
);
break;
default:
writeError('Cannot load script from unknown protocol \'' +
scriptLoc.protocol);
process.exit(1);
}
// Set up the context for the worker instance
var workerCtx = {};
// Context elements required for node.js
//
// XXX: There must be a better way to do this.
workerCtx.global = workerCtx;
workerCtx.process = process;
workerCtx.require = require;
workerCtx.console = console;
workerCtx.__filename = scriptLoc.pathname;
workerCtx.__dirname = path.dirname(scriptLoc.pathname);
workerCtx.setTimeout = setTimeout;
workerCtx.clearTimeout = clearTimeout;
workerCtx.setInterval = setInterval;
workerCtx.clearInterval = clearInterval;
// Context elements required by the WebWorkers API spec
workerCtx.postMessage = function(msg, fd) {
ms.send([wwutil.MSGTYPE_USER, msg], fd);
};
workerCtx.self = workerCtx;
workerCtx.location = scriptLoc;
workerCtx.closing = false;
workerCtx.close = function() {
process.exit(0);
};