Skip to content

Commit 7d8882b

Browse files
committed
handle_wrap: expose an isRefed() check to JS
This allows third-party tools to check whether or not a handle that can be unreferenced is unreferenced at a particular time. Notably, this should be helpful for inspection via AsyncWrap. Also, this is useful even to node's internals, particularly timers. Refs: #5828 Refs: #5827 PR-URL: #5834 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Trevor Norris <[email protected]>
1 parent 1879e1e commit 7d8882b

11 files changed

+146
-0
lines changed

src/handle_wrap.cc

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace node {
1111

12+
using v8::Boolean;
1213
using v8::Context;
1314
using v8::FunctionCallbackInfo;
1415
using v8::HandleScope;
@@ -37,6 +38,15 @@ void HandleWrap::Unref(const FunctionCallbackInfo<Value>& args) {
3738
}
3839

3940

41+
void HandleWrap::IsRefed(const FunctionCallbackInfo<Value>& args) {
42+
Environment* env = Environment::GetCurrent(args);
43+
HandleWrap* wrap = Unwrap<HandleWrap>(args.Holder());
44+
45+
bool refed = IsAlive(wrap) && (wrap->flags_ & kUnref) == 0;
46+
args.GetReturnValue().Set(refed);
47+
}
48+
49+
4050
void HandleWrap::Close(const FunctionCallbackInfo<Value>& args) {
4151
Environment* env = Environment::GetCurrent(args);
4252

src/handle_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class HandleWrap : public AsyncWrap {
3535
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
3636
static void Ref(const v8::FunctionCallbackInfo<v8::Value>& args);
3737
static void Unref(const v8::FunctionCallbackInfo<v8::Value>& args);
38+
static void IsRefed(const v8::FunctionCallbackInfo<v8::Value>& args);
3839

3940
static inline bool IsAlive(const HandleWrap* wrap) {
4041
return wrap != nullptr && wrap->GetHandle() != nullptr;

src/pipe_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ void PipeWrap::Initialize(Local<Object> target,
8181
env->SetProtoMethod(t, "close", HandleWrap::Close);
8282
env->SetProtoMethod(t, "unref", HandleWrap::Unref);
8383
env->SetProtoMethod(t, "ref", HandleWrap::Ref);
84+
env->SetProtoMethod(t, "isRefed", HandleWrap::IsRefed);
8485

8586
StreamWrap::AddMethods(env, t);
8687

src/process_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ProcessWrap : public HandleWrap {
4040

4141
env->SetProtoMethod(constructor, "ref", HandleWrap::Ref);
4242
env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);
43+
env->SetProtoMethod(constructor, "isRefed", HandleWrap::IsRefed);
4344

4445
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Process"),
4546
constructor->GetFunction());

src/signal_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class SignalWrap : public HandleWrap {
3232
env->SetProtoMethod(constructor, "close", HandleWrap::Close);
3333
env->SetProtoMethod(constructor, "ref", HandleWrap::Ref);
3434
env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);
35+
env->SetProtoMethod(constructor, "isRefed", HandleWrap::IsRefed);
3536
env->SetProtoMethod(constructor, "start", Start);
3637
env->SetProtoMethod(constructor, "stop", Stop);
3738

src/tcp_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ void TCPWrap::Initialize(Local<Object> target,
8888

8989
env->SetProtoMethod(t, "ref", HandleWrap::Ref);
9090
env->SetProtoMethod(t, "unref", HandleWrap::Unref);
91+
env->SetProtoMethod(t, "isRefed", HandleWrap::IsRefed);
9192

9293
StreamWrap::AddMethods(env, t, StreamBase::kFlagHasWritev);
9394

src/timer_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class TimerWrap : public HandleWrap {
3939
env->SetProtoMethod(constructor, "close", HandleWrap::Close);
4040
env->SetProtoMethod(constructor, "ref", HandleWrap::Ref);
4141
env->SetProtoMethod(constructor, "unref", HandleWrap::Unref);
42+
env->SetProtoMethod(constructor, "isRefed", HandleWrap::IsRefed);
4243

4344
env->SetProtoMethod(constructor, "start", Start);
4445
env->SetProtoMethod(constructor, "stop", Stop);

src/tty_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void TTYWrap::Initialize(Local<Object> target,
3737

3838
env->SetProtoMethod(t, "close", HandleWrap::Close);
3939
env->SetProtoMethod(t, "unref", HandleWrap::Unref);
40+
env->SetProtoMethod(t, "isRefed", HandleWrap::IsRefed);
4041

4142
StreamWrap::AddMethods(env, t, StreamBase::kFlagNoShutdown);
4243

src/udp_wrap.cc

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ void UDPWrap::Initialize(Local<Object> target,
108108

109109
env->SetProtoMethod(t, "ref", HandleWrap::Ref);
110110
env->SetProtoMethod(t, "unref", HandleWrap::Unref);
111+
env->SetProtoMethod(t, "isRefed", HandleWrap::IsRefed);
111112

112113
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "UDP"), t->GetFunction());
113114
env->set_udp_constructor_function(t->GetFunction());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const strictEqual = require('assert').strictEqual;
5+
const spawn = require('child_process').spawn;
6+
7+
function makeAssert(message) {
8+
return function(actual, expected) {
9+
strictEqual(actual, expected, message);
10+
};
11+
}
12+
const assert = makeAssert('isRefed() not working on tty_wrap');
13+
14+
if (process.argv[2] === 'child') {
15+
// Test tty_wrap in piped child to guarentee stdin being a TTY.
16+
const ReadStream = require('tty').ReadStream;
17+
const tty = new ReadStream(0);
18+
assert(Object.getPrototypeOf(tty._handle).hasOwnProperty('isRefed'), true);
19+
assert(tty._handle.isRefed(), true);
20+
tty.unref();
21+
assert(tty._handle.isRefed(), false);
22+
return;
23+
}
24+
25+
// Use spawn so that we can be sure that stdin has a _handle property.
26+
// Refs: https://github.com/nodejs/node/pull/5916
27+
const proc = spawn(process.execPath, [__filename, 'child'], { stdio: 'pipe' });
28+
proc.stderr.pipe(process.stderr);
29+
proc.on('exit', common.mustCall(function(exitCode) {
30+
process.exitCode = exitCode;
31+
}));
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const strictEqual = require('assert').strictEqual;
5+
6+
function makeAssert(message) {
7+
return function(actual, expected) {
8+
strictEqual(actual, expected, message);
9+
};
10+
}
11+
12+
13+
// child_process
14+
{
15+
const assert = makeAssert('isRefed() not working on process_wrap');
16+
const spawn = require('child_process').spawn;
17+
const cmd = common.isWindows ? 'rundll32' : 'ls';
18+
const cp = spawn(cmd);
19+
assert(Object.getPrototypeOf(cp._handle).hasOwnProperty('isRefed'), true);
20+
assert(cp._handle.isRefed(), true);
21+
cp.unref();
22+
assert(cp._handle.isRefed(), false);
23+
cp.ref();
24+
assert(cp._handle.isRefed(), true);
25+
cp.unref();
26+
}
27+
28+
29+
// dgram
30+
{
31+
const assert = makeAssert('isRefed() not working on udp_wrap');
32+
const dgram = require('dgram');
33+
34+
const sock4 = dgram.createSocket('udp4');
35+
assert(Object.getPrototypeOf(sock4._handle).hasOwnProperty('isRefed'), true);
36+
assert(sock4._handle.isRefed(), true);
37+
sock4.unref();
38+
assert(sock4._handle.isRefed(), false);
39+
sock4.ref();
40+
assert(sock4._handle.isRefed(), true);
41+
sock4.unref();
42+
43+
const sock6 = dgram.createSocket('udp6');
44+
assert(Object.getPrototypeOf(sock6._handle).hasOwnProperty('isRefed'), true);
45+
assert(sock6._handle.isRefed(), true);
46+
sock6.unref();
47+
assert(sock6._handle.isRefed(), false);
48+
sock6.ref();
49+
assert(sock6._handle.isRefed(), true);
50+
sock6.unref();
51+
}
52+
53+
54+
// pipe
55+
{
56+
const assert = makeAssert('isRefed() not working on pipe_wrap');
57+
const Pipe = process.binding('pipe_wrap').Pipe;
58+
const handle = new Pipe();
59+
assert(Object.getPrototypeOf(handle).hasOwnProperty('isRefed'), true);
60+
assert(handle.isRefed(), true);
61+
handle.unref();
62+
assert(handle.isRefed(), false);
63+
handle.ref();
64+
assert(handle.isRefed(), true);
65+
handle.unref();
66+
}
67+
68+
69+
// tcp
70+
{
71+
const assert = makeAssert('isRefed() not working on tcp_wrap');
72+
const net = require('net');
73+
const server = net.createServer(() => {}).listen(common.PORT);
74+
assert(Object.getPrototypeOf(server._handle).hasOwnProperty('isRefed'), true);
75+
assert(server._handle.isRefed(), true);
76+
assert(server._unref, false);
77+
server.unref();
78+
assert(server._handle.isRefed(), false);
79+
assert(server._unref, true);
80+
server.ref();
81+
assert(server._handle.isRefed(), true);
82+
assert(server._unref, false);
83+
server.unref();
84+
}
85+
86+
87+
// timers
88+
{
89+
const assert = makeAssert('isRefed() not working on timer_wrap');
90+
const timer = setTimeout(() => {}, 500);
91+
timer.unref();
92+
assert(Object.getPrototypeOf(timer._handle).hasOwnProperty('isRefed'), true);
93+
assert(timer._handle.isRefed(), false);
94+
timer.ref();
95+
assert(timer._handle.isRefed(), true);
96+
timer.unref();
97+
}

0 commit comments

Comments
 (0)