From f6a7fe26574defaa807a13248102ebe0f23270af Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 8 Jun 2009 16:17:33 +0200 Subject: [PATCH] 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. --- src/node.cc | 45 ++++++++++++++++++++----------------- src/node.js | 40 +++++++++++++++++++++++++++++---- test/fixtures/a.js | 10 ++++++++- test/fixtures/b/c.js | 9 +++++++- test/fixtures/b/d.js | 9 +++++++- test/test-module-loading.js | 19 ++++++++++++++++ website/api.html | 17 ++++++++++++++ 7 files changed, 122 insertions(+), 27 deletions(-) diff --git a/src/node.cc b/src/node.cc index 75783c81caa..37e6d84da72 100644 --- a/src/node.cc +++ b/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 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 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 exit_v = node->Get(String::New("exit")); + assert(exit_v->IsFunction()); + Handle exit_f = Handle::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; } diff --git a/src/node.js b/src/node.js index f96eeb719e5..d558f977bf9 100644 --- a/src/node.js +++ b/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); + }); + }; +}()) diff --git a/test/fixtures/a.js b/test/fixtures/a.js index d1405e2d16e..2d23232805c 100644 --- a/test/fixtures/a.js +++ b/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"; +} diff --git a/test/fixtures/b/c.js b/test/fixtures/b/c.js index 72b539e7296..e2091fdaa85 100644 --- a/test/fixtures/b/c.js +++ b/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"; +} diff --git a/test/fixtures/b/d.js b/test/fixtures/b/d.js index e7ae0e8d1a6..7e178e99b3c 100644 --- a/test/fixtures/b/d.js +++ b/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"; +} + diff --git a/test/test-module-loading.js b/test/test-module-loading.js index aa4c28823a9..519c516510e 100644 --- a/test/test-module-loading.js +++ b/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"); +} diff --git a/website/api.html b/website/api.html index d67e67a4e4b..3290ae9d9b1 100644 --- a/website/api.html +++ b/website/api.html @@ -944,6 +944,23 @@ function onLoad () { used after onLoad() is called. So put them at the beginning of your file.

+ +

+ Additionally when node.exit() is called or when + a program exits naturally, the function onExit() will be + called for each module (children first). + The onExit() 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. +

+ +

+ Just to reiterate: onExit(), is not the place to close + files or shutdown servers. The process will exit before they get + performed. +

+