From 5f0406534ca4a465d11892a747a38c0e5c884cf2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 28 Apr 2012 18:45:10 +0200 Subject: [PATCH] process: add _getActiveHandles(), _getActiveRequests() * process._getActiveHandles() returns a list containing all active handles (timers, sockets, etc.) that have not been unref'd. * process._getActiveRequests() returns a list of active requests (in-flight actions like connecting to a remote host, writing data to a socket, etc.). --- LICENSE | 28 ++++++ node.gyp | 1 + src/handle_wrap.cc | 8 ++ src/handle_wrap.h | 4 + src/ngx-queue.h | 106 +++++++++++++++++++++++ src/node.cc | 44 +++++++++- src/req_wrap.h | 9 +- test/simple/test-process-active-wraps.js | 54 ++++++++++++ 8 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 src/ngx-queue.h create mode 100644 test/simple/test-process-active-wraps.js diff --git a/LICENSE b/LICENSE index a6bc8813ea..f464affffc 100644 --- a/LICENSE +++ b/LICENSE @@ -550,3 +550,31 @@ maintained libraries. The externally maintained libraries used by Node are: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + +- src/ngx-queue.h ngx-queue.h is taken from the nginx source tree. nginx's + license follows + """ + Copyright (C) 2002-2012 Igor Sysoev + Copyright (C) 2011,2012 Nginx, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + """ diff --git a/node.gyp b/node.gyp index edcea98143..824b61e49f 100644 --- a/node.gyp +++ b/node.gyp @@ -107,6 +107,7 @@ 'src/node_script.h', 'src/node_string.h', 'src/node_version.h', + 'src/ngx-queue.h', 'src/pipe_wrap.h', 'src/req_wrap.h', 'src/slab_allocator.h', diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index d9621b4c4b..11777a88c3 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -20,10 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node.h" +#include "ngx-queue.h" #include "handle_wrap.h" namespace node { +using v8::Array; using v8::Object; using v8::Handle; using v8::Local; @@ -52,6 +54,10 @@ using v8::Integer; } +// defined in node.cc +extern ngx_queue_t handle_wrap_queue; + + void HandleWrap::Initialize(Handle target) { /* Doesn't do anything at the moment. */ } @@ -125,6 +131,7 @@ HandleWrap::HandleWrap(Handle object, uv_handle_t* h) { assert(object->InternalFieldCount() > 0); object_ = v8::Persistent::New(object); object_->SetPointerInInternalField(0, this); + ngx_queue_insert_tail(&handle_wrap_queue, &handle_wrap_queue_); } @@ -136,6 +143,7 @@ void HandleWrap::SetHandle(uv_handle_t* h) { HandleWrap::~HandleWrap() { assert(object_.IsEmpty()); + ngx_queue_remove(&handle_wrap_queue_); } diff --git a/src/handle_wrap.h b/src/handle_wrap.h index b9cf31e8eb..c6dd4c9d6a 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -22,6 +22,8 @@ #ifndef HANDLE_WRAP_H_ #define HANDLE_WRAP_H_ +#include "ngx-queue.h" + namespace node { // Rules: @@ -61,7 +63,9 @@ class HandleWrap { v8::Persistent object_; private: + friend v8::Handle GetActiveHandles(const v8::Arguments&); static void OnClose(uv_handle_t* handle); + ngx_queue_t handle_wrap_queue_; // Using double underscore due to handle_ member in tcp_wrap. Probably // tcp_wrap should rename it's member to 'handle'. uv_handle_t* handle__; diff --git a/src/ngx-queue.h b/src/ngx-queue.h new file mode 100644 index 0000000000..7058ce408d --- /dev/null +++ b/src/ngx-queue.h @@ -0,0 +1,106 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef NGX_QUEUE_H_INCLUDED_ +#define NGX_QUEUE_H_INCLUDED_ + + +typedef struct ngx_queue_s ngx_queue_t; + +struct ngx_queue_s { + ngx_queue_t *prev; + ngx_queue_t *next; +}; + + +#define ngx_queue_init(q) \ + (q)->prev = q; \ + (q)->next = q + + +#define ngx_queue_empty(h) \ + (h == (h)->prev) + + +#define ngx_queue_insert_head(h, x) \ + (x)->next = (h)->next; \ + (x)->next->prev = x; \ + (x)->prev = h; \ + (h)->next = x + + +#define ngx_queue_insert_after ngx_queue_insert_head + + +#define ngx_queue_insert_tail(h, x) \ + (x)->prev = (h)->prev; \ + (x)->prev->next = x; \ + (x)->next = h; \ + (h)->prev = x + + +#define ngx_queue_head(h) \ + (h)->next + + +#define ngx_queue_last(h) \ + (h)->prev + + +#define ngx_queue_sentinel(h) \ + (h) + + +#define ngx_queue_next(q) \ + (q)->next + + +#define ngx_queue_prev(q) \ + (q)->prev + + +#if (NGX_DEBUG) + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next; \ + (x)->prev = NULL; \ + (x)->next = NULL + +#else + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next + +#endif + + +#define ngx_queue_split(h, q, n) \ + (n)->prev = (h)->prev; \ + (n)->prev->next = n; \ + (n)->next = q; \ + (h)->prev = (q)->prev; \ + (h)->prev->next = h; \ + (q)->prev = n; + + +#define ngx_queue_add(h, n) \ + (h)->prev->next = (n)->next; \ + (n)->next->prev = (h)->prev; \ + (h)->prev = (n)->prev; \ + (h)->prev->next = h; + + +#define ngx_queue_data(q, type, link) \ + (type *) ((unsigned char *) q - offsetof(type, link)) + + +#define ngx_queue_foreach(q, h) \ + for ((q) = ngx_queue_head(h); (q) != (h); (q) = ngx_queue_next(q)) + + +#endif /* NGX_QUEUE_H_INCLUDED_ */ diff --git a/src/node.cc b/src/node.cc index bca0ea28eb..41dab06e52 100644 --- a/src/node.cc +++ b/src/node.cc @@ -20,7 +20,8 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node.h" -#include "handle_wrap.h" // HandleWrap::GetActiveHandles() +#include "req_wrap.h" +#include "handle_wrap.h" #include "uv.h" @@ -91,6 +92,9 @@ extern char **environ; namespace node { +ngx_queue_t handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue }; +ngx_queue_t req_wrap_queue = { &req_wrap_queue, &req_wrap_queue }; + // declared in req_wrap.h Persistent process_symbol; Persistent domain_symbol; @@ -1332,6 +1336,42 @@ Local ExecuteString(Handle source, Handle filename) { } +static Handle GetActiveRequests(const Arguments& args) { + HandleScope scope; + + Local ary = Array::New(); + ngx_queue_t* q = NULL; + int i = 0; + + ngx_queue_foreach(q, &req_wrap_queue) { + ReqWrap* w = container_of(q, ReqWrap, req_wrap_queue_); + if (w->object_.IsEmpty()) continue; + ary->Set(i++, w->object_); + } + + return scope.Close(ary); +} + + +// Non-static, friend of HandleWrap. Could have been a HandleWrap method but +// implemented here for consistency with GetActiveRequests(). +Handle GetActiveHandles(const Arguments& args) { + HandleScope scope; + + Local ary = Array::New(); + ngx_queue_t* q = NULL; + int i = 0; + + ngx_queue_foreach(q, &handle_wrap_queue) { + HandleWrap* w = container_of(q, HandleWrap, handle_wrap_queue_); + if (w->object_.IsEmpty() || w->unref) continue; + ary->Set(i++, w->object_); + } + + return scope.Close(ary); +} + + static Handle Abort(const Arguments& args) { abort(); return Undefined(); @@ -2237,6 +2277,8 @@ Handle SetupProcessObject(int argc, char *argv[]) { // define various internal methods + NODE_SET_METHOD(process, "_getActiveRequests", GetActiveRequests); + NODE_SET_METHOD(process, "_getActiveHandles", GetActiveHandles); NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback); NODE_SET_METHOD(process, "reallyExit", Exit); NODE_SET_METHOD(process, "abort", Abort); diff --git a/src/req_wrap.h b/src/req_wrap.h index 11c7d12044..ba56821bbe 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -22,11 +22,14 @@ #ifndef REQ_WRAP_H_ #define REQ_WRAP_H_ +#include "ngx-queue.h" + namespace node { // defined in node.cc extern v8::Persistent process_symbol; extern v8::Persistent domain_symbol; +extern ngx_queue_t req_wrap_queue; template class ReqWrap { @@ -45,10 +48,13 @@ class ReqWrap { // fprintf(stderr, "setting domain on ReqWrap\n"); object_->Set(domain_symbol, domain); } + + ngx_queue_insert_tail(&req_wrap_queue, &req_wrap_queue_); } ~ReqWrap() { + ngx_queue_remove(&req_wrap_queue_); // Assert that someone has called Dispatched() assert(req_.data == this); assert(!object_.IsEmpty()); @@ -62,8 +68,9 @@ class ReqWrap { } v8::Persistent object_; - T req_; + ngx_queue_t req_wrap_queue_; void* data_; + T req_; // *must* be last, GetActiveRequests() in node.cc depends on it }; diff --git a/test/simple/test-process-active-wraps.js b/test/simple/test-process-active-wraps.js new file mode 100644 index 0000000000..254e0bf63a --- /dev/null +++ b/test/simple/test-process-active-wraps.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var spawn = require('child_process').spawn; +var net = require('net'); + +function expect(activeHandles, activeRequests) { + assert.equal(process._getActiveHandles().length, activeHandles); + assert.equal(process._getActiveRequests().length, activeRequests); +} + +(function() { + expect(0, 0); + var server = net.createServer().listen(common.PORT); + expect(1, 0); + server.close(); + expect(1, 0); // server handle doesn't shut down until next tick +})(); + +(function() { + expect(1, 0); + var conn = net.createConnection(common.PORT); + conn.on('error', function() { /* ignore */ }); + expect(2, 1); + conn.destroy(); + expect(2, 1); // client handle doesn't shut down until next tick +})(); + +process.nextTick(function() { + process.nextTick(function() { + // the handles should be gone but the connect req could still be alive + assert.equal(process._getActiveHandles().length, 0); + }); +});