child_process: add isolates support
authorBen Noordhuis <info@bnoordhuis.nl>
Thu, 5 Jan 2012 23:42:10 +0000 (00:42 +0100)
committerBen Noordhuis <info@bnoordhuis.nl>
Thu, 5 Jan 2012 23:47:14 +0000 (00:47 +0100)
Passing an options object with {thread:true} to .fork() or .spawn() will run the
target script in a thread instead of a separate process.

lib/child_process.js
src/node.js

index 2e39166..5a3374c 100644 (file)
@@ -167,12 +167,6 @@ exports.fork = function(modulePath, args, options) {
   args = args ? args.slice(0) : [];
   args.unshift(modulePath);
 
-  if (options.thread) {
-    if (!process.features.isolates) {
-      throw new Error('node compiled without isolate support');
-    }
-  }
-
   if (options.stdinStream) {
     throw new Error('stdinStream not allowed for fork()');
   }
@@ -191,11 +185,11 @@ exports.fork = function(modulePath, args, options) {
   options.env.NODE_CHANNEL_FD = 42;
 
   // stdin is the IPC channel.
-  options.stdinStream = createPipe(true);
+  if (!options.thread) options.stdinStream = createPipe(true);
 
   var child = spawn(process.execPath, args, options);
 
-  setupChannel(child, options.stdinStream);
+  if (!options.thread) setupChannel(child, options.stdinStream);
 
   child.on('exit', function() {
     if (child._channel) {
@@ -358,7 +352,7 @@ var spawn = exports.spawn = function(file, args, options) {
     envPairs.push(key + '=' + env[key]);
   }
 
-  var child = new ChildProcess();
+  var child = (options && options.thread) ? (new Isolate) : (new ChildProcess);
 
   child.spawn({
     file: file,
@@ -520,3 +514,55 @@ ChildProcess.prototype.kill = function(sig) {
     // TODO: raise error if r == -1?
   }
 };
+
+
+// Lazy loaded.
+var isolates = null;
+
+
+function Isolate() {
+  if (!process.features.isolates) {
+    throw new Error('Compiled without isolates support.');
+  }
+
+  if (!isolates) {
+    isolates = process.binding('isolates');
+  }
+
+  this._handle = null;
+}
+inherits(Isolate, EventEmitter); // maybe inherit from ChildProcess?
+
+
+Isolate.prototype.spawn = function(options) {
+  var self = this;
+
+  if (self._handle) throw new Error('Isolate already running.');
+  self._handle = isolates.create(options.args);
+  if (!self._handle) throw new Error('Cannot create isolate.');
+
+  self._handle.onmessage = function(msg) {
+    msg = JSON.parse('' + msg);
+    self.emit('message', msg);
+  };
+
+  self._handle.onexit = function() {
+    self._handle = null;
+    self.emit('exit');
+  };
+};
+
+
+Isolate.prototype.kill = function(sig) {
+  if (!this._handle) throw new Error('Isolate not running.');
+  // ignore silently for now, need a way to signal the other thread
+};
+
+
+Isolate.prototype.send = function(msg) {
+  if (typeof msg === 'undefined') throw new TypeError('Bad argument.');
+  if (!this._handle) throw new Error('Isolate not running.');
+  msg = JSON.stringify(msg);
+  msg = new Buffer(msg);
+  return this._handle.send(msg);
+};
index 1f159d6..1be0490 100644 (file)
         });
       }
     }
+
+    if (process.tid === 1) return;
+
+    // isolate initialization
+    process.send = function(msg) {
+      if (typeof msg === 'undefined') throw new TypeError('Bad argument.');
+      msg = JSON.stringify(msg);
+      msg = new Buffer(msg);
+      return process._send(msg);
+    };
+
+    process._onmessage = function(msg) {
+      msg = JSON.parse('' + msg);
+      process.emit('message', msg);
+    };
   }
 
   startup.globalVariables = function() {