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:
parent
b6fe4aec50
commit
f6a7fe2657
45
src/node.cc
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;
|
||||
}
|
||||
|
40
src/node.js
40
src/node.js
@ -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
10
test/fixtures/a.js
vendored
@ -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
vendored
9
test/fixtures/b/c.js
vendored
@ -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
vendored
9
test/fixtures/b/d.js
vendored
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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…
Reference in New Issue
Block a user