Browse Source

Implement onExit() hook for modules.

onExit() is similar to the onLoad() callback. onExit() is called on each
module just before the process exits. This can be used to check state in
unit tests, but not to perform I/O. The process will forcibly exit as soon
as all of the onExit callbacks are made.
v0.7.4-release
Ryan 16 years ago
parent
commit
f6a7fe2657
  1. 45
      src/node.cc
  2. 38
      src/node.js
  3. 10
      test/fixtures/a.js
  4. 9
      test/fixtures/b/c.js
  5. 9
      test/fixtures/b/d.js
  6. 19
      test/test-module-loading.js
  7. 17
      website/api.html

45
src/node.cc

@ -21,8 +21,6 @@ using namespace v8;
using namespace node;
using namespace std;
static int exit_code = 0;
ObjectWrap::~ObjectWrap ( )
{
handle_->SetInternalField(0, Undefined());
@ -265,6 +263,20 @@ node::ParseEncoding (Handle<Value> encoding_v)
}
}
static void
ExecuteNativeJS (const char *filename, const char *data)
{
HandleScope scope;
TryCatch try_catch;
ExecuteString(String::New(data), String::New(filename));
if (try_catch.HasCaught()) {
puts("There is an error in Node's built-in javascript");
puts("This should be reported as a bug!");
ReportException(&try_catch);
exit(1);
}
}
int
main (int argc, char *argv[])
{
@ -299,7 +311,7 @@ main (int argc, char *argv[])
NODE_SET_METHOD(node, "compile", compile); // internal
NODE_SET_METHOD(node, "debug", debug);
NODE_SET_METHOD(node, "exit", node_exit);
NODE_SET_METHOD(node, "reallyExit", node_exit);
Local<Array> arguments = Array::New(argc);
for (int i = 0; i < argc; i++) {
@ -308,7 +320,6 @@ main (int argc, char *argv[])
}
g->Set(String::New("ARGV"), arguments);
// BUILT-IN MODULES
Timer::Initialize(node);
@ -330,20 +341,18 @@ main (int argc, char *argv[])
HTTPServer::Initialize(http);
HTTPConnection::Initialize(http);
// NATIVE JAVASCRIPT MODULES
TryCatch try_catch;
ExecuteString(String::New(native_http), String::New("http.js"));
if (try_catch.HasCaught()) goto native_js_error;
ExecuteString(String::New(native_file), String::New("file.js"));
if (try_catch.HasCaught()) goto native_js_error;
ExecuteString(String::New(native_node), String::New("node.js"));
if (try_catch.HasCaught()) goto native_js_error;
ExecuteNativeJS("http.js", native_http);
ExecuteNativeJS("file.js", native_file);
ExecuteNativeJS("node.js", native_node);
ev_loop(EV_DEFAULT_UC_ 0);
// call node.exit()
Local<Value> exit_v = node->Get(String::New("exit"));
assert(exit_v->IsFunction());
Handle<Function> exit_f = Handle<Function>::Cast(exit_v);
exit_f->Call(g, 0, NULL);
context.Dispose();
// The following line when uncommented causes an error.
// To reproduce do this:
@ -353,9 +362,5 @@ main (int argc, char *argv[])
//
//V8::Dispose();
return exit_code;
native_js_error:
ReportException(&try_catch);
return 1;
return 0;
}

38
src/node.js

@ -138,9 +138,41 @@ node.Module.prototype.loadChildren = function (callback) {
}
};
node.Module.prototype.exitChildren = function (callback) {
var children = this.children;
if (children.length == 0 && callback) callback();
var nexited = 0;
for (var i = 0; i < children.length; i++) {
children[i].exit(function () {
nexited += 1;
if (nexited == children.length && callback) callback();
});
}
};
node.Module.prototype.exit = function (callback) {
throw "not implemented";
var self = this;
if (self.exited)
throw "Module '" + self.filename + "' is already exited.";
this.exitChildren(function () {
if (self.onExit) {
self.onExit();
}
self.exited = true;
if (callback) callback()
});
};
// Load the root module. I.E. the command line argument.
(new node.Module({ path: ARGV[1], target: this })).load();
(function () {
// Load the root module. I.E. the command line argument.
root_module = new node.Module({ path: ARGV[1], target: this });
root_module.load();
node.exit = function (code) {
root_module.exit(function () {
node.reallyExit(code);
});
};
}())

10
test/fixtures/a.js

@ -1,10 +1,18 @@
var c = require("b/c.js");
var string = "A";
exports.A = function () {
return "A";
return string;
};
exports.C = function () {
return c.C();
};
exports.D = function () {
return c.D();
};
function onExit () {
string = "A done";
}

9
test/fixtures/b/c.js

@ -1,9 +1,16 @@
var d = require("d.js");
var string = "C";
exports.C = function () {
return "C";
return string;
};
exports.D = function () {
return d.D();
};
function onExit () {
puts("c.js onExit called");
string = "C done";
}

9
test/fixtures/b/d.js

@ -1,4 +1,11 @@
var string = "D";
exports.D = function () {
return "D";
return string;
};
function onExit () {
node.debug("d.js onExit called");
string = "D done";
}

19
test/test-module-loading.js

@ -21,3 +21,22 @@ function onLoad () {
assertInstanceof(d2.D, Function);
assertEquals("D", d2.D());
}
function onExit () {
assertInstanceof(a.A, Function);
assertEquals("A done", a.A());
assertInstanceof(a.C, Function);
assertEquals("C done", a.C());
assertInstanceof(a.D, Function);
assertEquals("D done", a.D());
assertInstanceof(d.D, Function);
assertEquals("D done", d.D());
assertInstanceof(d2.D, Function);
assertEquals("D done", d2.D());
node.debug("test-module-loading.js onExit() called");
}

17
website/api.html

@ -944,6 +944,23 @@ function onLoad () {
used after <code>onLoad()</code> is called. So put them at the
beginning of your file.
</p>
<p>
Additionally when <code>node.exit()</code> is called or when
a program exits naturally, the function <code>onExit()</code> will be
called for each module (children first).
The <code>onExit()</code> callback cannot perform I/O as the process is
going to forcably exit in several microseconds, however it is a good
hook to perform some constant time checks of the module's state.
It's useful for unit tests.
</p>
<p>
Just to reiterate: <code>onExit()</code>, is not the place to close
files or shutdown servers. The process will exit before they get
performed.
</p>
</div>
</body>
</html>

Loading…
Cancel
Save