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 | 38 ++++++++++++++++++++++++++++--- 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, 121 insertions(+), 26 deletions(-) diff --git a/src/node.cc b/src/node.cc index 75783c81ca..37e6d84da7 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 f96eeb719e..d558f977bf 100644 --- a/src/node.js +++ b/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); + }); + }; +}()) diff --git a/test/fixtures/a.js b/test/fixtures/a.js index d1405e2d16..2d23232805 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 72b539e729..e2091fdaa8 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 e7ae0e8d1a..7e178e99b3 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 aa4c28823a..519c516510 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 d67e67a4e4..3290ae9d9b 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. +

+