0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00

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.
This commit is contained in:
Ryan 2009-06-08 16:17:33 +02:00
parent b6fe4aec50
commit f6a7fe2657
7 changed files with 122 additions and 27 deletions

View File

@ -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;
}

View File

@ -138,9 +138,41 @@ node.Module.prototype.loadChildren = function (callback) {
}
};
node.Module.prototype.exit = function (callback) {
throw "not implemented";
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();
});
}
};
// Load the root module. I.E. the command line argument.
(new node.Module({ path: ARGV[1], target: this })).load();
node.Module.prototype.exit = function (callback) {
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()
});
};
(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 vendored
View File

@ -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";
}

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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");
}

View File

@ -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>