Skip to content

Commit d35333a

Browse files
jasnelladuh95
authored andcommitted
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: #53266 PR-URL: #56400 Backport-PR-URL: #56571 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent d33eaf2 commit d35333a

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

doc/api/process.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,6 +3221,23 @@ const { ppid } = require('node:process');
32213221
console.log(`The parent process is pid ${ppid}`);
32223222
```
32233223
3224+
## `process.ref(maybeRefable)`
3225+
3226+
<!-- YAML
3227+
added: REPLACEME
3228+
-->
3229+
3230+
* `maybeRefable` {any} An object that may be "refable".
3231+
3232+
An object is "refable" if it implements the Node.js "Refable protocol".
3233+
Specifically, this means that the object implements the `Symbol.for('node:ref')`
3234+
and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js
3235+
event loop alive, while "unref'd" objects will not. Historically, this was
3236+
implemented by using `ref()` and `unref()` methods directly on the objects.
3237+
This pattern, however, is being deprecated in favor of the "Refable protocol"
3238+
in order to better support Web Platform API types whose APIs cannot be modified
3239+
to add `ref()` and `unref()` methods but still need to support that behavior.
3240+
32243241
## `process.release`
32253242
32263243
<!-- YAML
@@ -4261,6 +4278,23 @@ console.log(
42614278
42624279
In [`Worker`][] threads, `process.umask(mask)` will throw an exception.
42634280
4281+
## `process.unref(maybeRefable)`
4282+
4283+
<!-- YAML
4284+
added: REPLACEME
4285+
-->
4286+
4287+
* `maybeUnfefable` {any} An object that may be "unref'd".
4288+
4289+
An object is "unrefable" if it implements the Node.js "Refable protocol".
4290+
Specifically, this means that the object implements the `Symbol.for('node:ref')`
4291+
and `Symbol.for('node:unref')` methods. "Ref'd" objects will keep the Node.js
4292+
event loop alive, while "unref'd" objects will not. Historically, this was
4293+
implemented by using `ref()` and `unref()` methods directly on the objects.
4294+
This pattern, however, is being deprecated in favor of the "Refable protocol"
4295+
in order to better support Web Platform API types whose APIs cannot be modified
4296+
to add `ref()` and `unref()` methods but still need to support that behavior.
4297+
42644298
## `process.uptime()`
42654299
42664300
<!-- YAML

lib/internal/bootstrap/node.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ const rawMethods = internalBinding('process_methods');
181181
process.availableMemory = rawMethods.availableMemory;
182182
process.kill = wrapped.kill;
183183
process.exit = wrapped.exit;
184+
process.ref = perThreadSetup.ref;
185+
process.unref = perThreadSetup.unref;
184186

185187
let finalizationMod;
186188
ObjectDefineProperty(process, 'finalization', {

lib/internal/process/per_thread.js

Lines changed: 14 additions & 0 deletions
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

@@ -422,6 +424,16 @@ function toggleTraceCategoryState(asyncHooksEnabled) {
422424

423425
const { arch, platform, version } = process;
424426

427+
function ref(maybeRefable) {
428+
const fn = maybeRefable?.[SymbolFor('node:ref')] || maybeRefable?.ref;
429+
if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable);
430+
}
431+
432+
function unref(maybeRefable) {
433+
const fn = maybeRefable?.[SymbolFor('node:unref')] || maybeRefable?.unref;
434+
if (typeof fn === 'function') FunctionPrototypeCall(fn, maybeRefable);
435+
}
436+
425437
module.exports = {
426438
toggleTraceCategoryState,
427439
assert,
@@ -432,4 +444,6 @@ module.exports = {
432444
arch,
433445
platform,
434446
version,
447+
ref,
448+
unref,
435449
};
Lines changed: 60 additions & 0 deletions
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)