Skip to content

Commit 35742a2

Browse files
committed
process: add process.ref() and process.unref() methods
The `process.ref(...)` and `process.unref(...)` methods are intended to replace the use of `ref()` and `unref()` methods defined directly on individual API objects. The existing `ref()` and `unref()` methods will be marked as legacy and won't be removed but new APIs should use `process.ref()` and `process.unref()` instead. Refs: nodejs#53266 PR-URL: nodejs#56400 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Chemi Atlow <[email protected]>
1 parent c5aa8b8 commit 35742a2

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

doc/api/process.md

+34
Original file line numberDiff line numberDiff line change
@@ -3232,6 +3232,23 @@ const { ppid } = require('node:process');
32323232
console.log(`The parent process is pid ${ppid}`);
32333233
```
32343234
3235+
## `process.ref(maybeRefable)`
3236+
3237+
<!-- YAML
3238+
added: REPLACEME
3239+
-->
3240+
3241+
* `maybeRefable` {any} An object that may be "refable".
3242+
3243+
An object is "refable" if it implements the Node.js "Refable protocol".
3244+
Specifically, this means that the object implements the `Symbol.for('node:ref')`
3245+
and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js
3246+
event loop alive, while "unref'd" objects will not. Historically, this was
3247+
implemented by using `ref()` and `unref()` methods directly on the objects.
3248+
This pattern, however, is being deprecated in favor of the "Refable protocol"
3249+
in order to better support Web Platform API types whose APIs cannot be modified
3250+
to add `ref()` and `unref()` methods but still need to support that behavior.
3251+
32353252
## `process.release`
32363253
32373254
<!-- YAML
@@ -4272,6 +4289,23 @@ console.log(
42724289
42734290
In [`Worker`][] threads, `process.umask(mask)` will throw an exception.
42744291
4292+
## `process.unref(maybeRefable)`
4293+
4294+
<!-- YAML
4295+
added: REPLACEME
4296+
-->
4297+
4298+
* `maybeUnfefable` {any} An object that may be "unref'd".
4299+
4300+
An object is "unrefable" if it implements the Node.js "Refable protocol".
4301+
Specifically, this means that the object implements the `Symbol.for('node:ref')`
4302+
and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js
4303+
event loop alive, while "unref'd" objects will not. Historically, this was
4304+
implemented by using `ref()` and `unref()` methods directly on the objects.
4305+
This pattern, however, is being deprecated in favor of the "Refable protocol"
4306+
in order to better support Web Platform API types whose APIs cannot be modified
4307+
to add `ref()` and `unref()` methods but still need to support that behavior.
4308+
42754309
## `process.uptime()`
42764310
42774311
<!-- YAML

lib/internal/bootstrap/node.js

+2
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ const rawMethods = internalBinding('process_methods');
178178
process.availableMemory = rawMethods.availableMemory;
179179
process.kill = wrapped.kill;
180180
process.exit = wrapped.exit;
181+
process.ref = perThreadSetup.ref;
182+
process.unref = perThreadSetup.unref;
181183

182184
let finalizationMod;
183185
ObjectDefineProperty(process, 'finalization', {

lib/internal/process/per_thread.js

+14
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const {
1313
ArrayPrototypeSplice,
1414
BigUint64Array,
1515
Float64Array,
16+
FunctionPrototypeCall,
1617
NumberMAX_SAFE_INTEGER,
1718
ObjectDefineProperty,
1819
ObjectFreeze,
@@ -26,6 +27,7 @@ const {
2627
StringPrototypeReplace,
2728
StringPrototypeSlice,
2829
Symbol,
30+
SymbolFor,
2931
SymbolIterator,
3032
} = primordials;
3133

@@ -418,6 +420,16 @@ function toggleTraceCategoryState(asyncHooksEnabled) {
418420

419421
const { arch, platform, version } = process;
420422

423+
function ref(maybeRefable) {
424+
const fn = maybeRefable?.[SymbolFor('node:ref')] || maybeRefable?.ref;
425+
if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable);
426+
}
427+
428+
function unref(maybeRefable) {
429+
const fn = maybeRefable?.[SymbolFor('node:unref')] || maybeRefable?.unref;
430+
if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable);
431+
}
432+
421433
module.exports = {
422434
toggleTraceCategoryState,
423435
buildAllowedFlags,
@@ -427,4 +439,6 @@ module.exports = {
427439
arch,
428440
platform,
429441
version,
442+
ref,
443+
unref,
430444
};
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const {
6+
describe,
7+
it,
8+
} = require('node:test');
9+
10+
const {
11+
strictEqual,
12+
} = require('node:assert');
13+
14+
class Foo {
15+
refCalled = 0;
16+
unrefCalled = 0;
17+
ref() {
18+
this.refCalled++;
19+
}
20+
unref() {
21+
this.unrefCalled++;
22+
}
23+
}
24+
25+
class Foo2 {
26+
refCalled = 0;
27+
unrefCalled = 0;
28+
[Symbol.for('node:ref')]() {
29+
this.refCalled++;
30+
}
31+
[Symbol.for('node:unref')]() {
32+
this.unrefCalled++;
33+
}
34+
}
35+
36+
describe('process.ref/unref work as expected', () => {
37+
it('refs...', () => {
38+
// Objects that implement the new Symbol-based API
39+
// just work.
40+
const foo1 = new Foo();
41+
const foo2 = new Foo2();
42+
process.ref(foo1);
43+
process.unref(foo1);
44+
process.ref(foo2);
45+
process.unref(foo2);
46+
strictEqual(foo1.refCalled, 1);
47+
strictEqual(foo1.unrefCalled, 1);
48+
strictEqual(foo2.refCalled, 1);
49+
strictEqual(foo2.unrefCalled, 1);
50+
51+
// Objects that implement the legacy API also just work.
52+
const i = setInterval(() => {}, 1000);
53+
strictEqual(i.hasRef(), true);
54+
process.unref(i);
55+
strictEqual(i.hasRef(), false);
56+
process.ref(i);
57+
strictEqual(i.hasRef(), true);
58+
clearInterval(i);
59+
});
60+
});

0 commit comments

Comments
 (0)