Skip to content

Commit 35d0c36

Browse files
H4adpull[bot]
authored andcommitted
process: port on-exit-leak-free to core
PR-URL: #53239 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Paolo Insogna <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 8f06bf5 commit 35d0c36

File tree

12 files changed

+569
-1
lines changed

12 files changed

+569
-1
lines changed

LICENSE

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2378,3 +2378,28 @@ The externally maintained libraries used by Node.js are:
23782378
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23792379
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23802380
"""
2381+
2382+
- on-exit-leak-free, located at lib/internal/process/finalization, is licensed as follows:
2383+
"""
2384+
MIT License
2385+
2386+
Copyright (c) 2021 Matteo Collina
2387+
2388+
Permission is hereby granted, free of charge, to any person obtaining a copy
2389+
of this software and associated documentation files (the "Software"), to deal
2390+
in the Software without restriction, including without limitation the rights
2391+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2392+
copies of the Software, and to permit persons to whom the Software is
2393+
furnished to do so, subject to the following conditions:
2394+
2395+
The above copyright notice and this permission notice shall be included in all
2396+
copies or substantial portions of the Software.
2397+
2398+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2399+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2400+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2401+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2402+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2403+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2404+
SOFTWARE.
2405+
"""

doc/api/process.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,219 @@ a code.
18971897
Specifying a code to [`process.exit(code)`][`process.exit()`] will override any
18981898
previous setting of `process.exitCode`.
18991899
1900+
## `process.finalization.register(ref, callback)`
1901+
1902+
<!-- YAML
1903+
added: REPLACEME
1904+
-->
1905+
1906+
> Stability: 1.1 - Active Development
1907+
1908+
* `ref` {Object | Function} The reference to the resource that is being tracked.
1909+
* `callback` {Function} The callback function to be called when the resource
1910+
is finalized.
1911+
* `ref` {Object | Function} The reference to the resource that is being tracked.
1912+
* `event` {string} The event that triggered the finalization. Defaults to 'exit'.
1913+
1914+
This function registers a callback to be called when the process emits the `exit`
1915+
event if the `ref` object was not garbage collected. If the object `ref` was garbage collected
1916+
before the `exit` event is emitted, the callback will be removed from the finalization registry,
1917+
and it will not be called on process exit.
1918+
1919+
Inside the callback you can release the resources allocated by the `ref` object.
1920+
Be aware that all limitations applied to the `beforeExit` event are also applied to the `callback` function,
1921+
this means that there is a possibility that the callback will not be called under special circumstances.
1922+
1923+
The idea of ​​this function is to help you free up resources when the starts process exiting,
1924+
but also let the object be garbage collected if it is no longer being used.
1925+
1926+
Eg: you can register an object that contains a buffer, you want to make sure that buffer is released
1927+
when the process exit, but if the object is garbage collected before the process exit, we no longer
1928+
need to release the buffer, so in this case we just remove the callback from the finalization registry.
1929+
1930+
```cjs
1931+
const { finalization } = require('node:process');
1932+
1933+
// Please make sure that the function passed to finalization.register()
1934+
// does not create a closure around unnecessary objects.
1935+
function onFinalize(obj, event) {
1936+
// You can do whatever you want with the object
1937+
obj.dispose();
1938+
}
1939+
1940+
function setup() {
1941+
// This object can be safely garbage collected,
1942+
// and the resulting shutdown function will not be called.
1943+
// There are no leaks.
1944+
const myDisposableObject = {
1945+
dispose() {
1946+
// Free your resources synchronously
1947+
},
1948+
};
1949+
1950+
finalization.register(myDisposableObject, onFinalize);
1951+
}
1952+
1953+
setup();
1954+
```
1955+
1956+
```mjs
1957+
import { finalization } from 'node:process';
1958+
1959+
// Please make sure that the function passed to finalization.register()
1960+
// does not create a closure around unnecessary objects.
1961+
function onFinalize(obj, event) {
1962+
// You can do whatever you want with the object
1963+
obj.dispose();
1964+
}
1965+
1966+
function setup() {
1967+
// This object can be safely garbage collected,
1968+
// and the resulting shutdown function will not be called.
1969+
// There are no leaks.
1970+
const myDisposableObject = {
1971+
dispose() {
1972+
// Free your resources synchronously
1973+
},
1974+
};
1975+
1976+
finalization.register(myDisposableObject, onFinalize);
1977+
}
1978+
1979+
setup();
1980+
```
1981+
1982+
The code above relies on the following assumptions:
1983+
1984+
* arrow functions are avoided
1985+
* regular functions are recommended to be within the global context (root)
1986+
1987+
Regular functions _could_ reference the context where the `obj` lives, making the `obj` not garbage collectible.
1988+
1989+
Arrow functions will hold the previous context. Consider, for example:
1990+
1991+
```js
1992+
class Test {
1993+
constructor() {
1994+
finalization.register(this, (ref) => ref.dispose());
1995+
1996+
// even something like this is highly discouraged
1997+
// finalization.register(this, () => this.dispose());
1998+
}
1999+
dispose() {}
2000+
}
2001+
```
2002+
2003+
It is very unlikely (not impossible) that this object will be garbage collected,
2004+
but if it is not, `dispose` will be called when `process.exit` is called.
2005+
2006+
Be careful and avoid relying on this feature for the disposal of critical resources,
2007+
as it is not guaranteed that the callback will be called under all circumstances.
2008+
2009+
## `process.finalization.registerBeforeExit(ref, callback)`
2010+
2011+
<!-- YAML
2012+
added: REPLACEME
2013+
-->
2014+
2015+
> Stability: 1.1 - Active Development
2016+
2017+
* `ref` {Object | Function} The reference
2018+
to the resource that is being tracked.
2019+
* `callback` {Function} The callback function to be called when the resource
2020+
is finalized.
2021+
* `ref` {Object | Function} The reference to the resource that is being tracked.
2022+
* `event` {string} The event that triggered the finalization. Defaults to 'beforeExit'.
2023+
2024+
This function behaves exactly like the `register`, except that the callback will be called
2025+
when the process emits the `beforeExit` event if `ref` object was not garbage collected.
2026+
2027+
Be aware that all limitations applied to the `beforeExit` event are also applied to the `callback` function,
2028+
this means that there is a possibility that the callback will not be called under special circumstances.
2029+
2030+
## `process.finalization.unregister(ref)`
2031+
2032+
<!-- YAML
2033+
added: REPLACEME
2034+
-->
2035+
2036+
> Stability: 1.1 - Active Development
2037+
2038+
* `ref` {Object | Function} The reference
2039+
to the resource that was registered previously.
2040+
2041+
This function remove the register of the object from the finalization
2042+
registry, so the callback will not be called anymore.
2043+
2044+
```cjs
2045+
const { finalization } = require('node:process');
2046+
2047+
// Please make sure that the function passed to finalization.register()
2048+
// does not create a closure around unnecessary objects.
2049+
function onFinalize(obj, event) {
2050+
// You can do whatever you want with the object
2051+
obj.dispose();
2052+
}
2053+
2054+
function setup() {
2055+
// This object can be safely garbage collected,
2056+
// and the resulting shutdown function will not be called.
2057+
// There are no leaks.
2058+
const myDisposableObject = {
2059+
dispose() {
2060+
// Free your resources synchronously
2061+
},
2062+
};
2063+
2064+
finalization.register(myDisposableObject, onFinalize);
2065+
2066+
// Do something
2067+
2068+
myDisposableObject.dispose();
2069+
finalization.unregister(myDisposableObject);
2070+
}
2071+
2072+
setup();
2073+
```
2074+
2075+
```mjs
2076+
import { finalization } from 'node:process';
2077+
2078+
// Please make sure that the function passed to finalization.register()
2079+
// does not create a closure around unnecessary objects.
2080+
function onFinalize(obj, event) {
2081+
// You can do whatever you want with the object
2082+
obj.dispose();
2083+
}
2084+
2085+
function setup() {
2086+
// This object can be safely garbage collected,
2087+
// and the resulting shutdown function will not be called.
2088+
// There are no leaks.
2089+
const myDisposableObject = {
2090+
dispose() {
2091+
// Free your resources synchronously
2092+
},
2093+
};
2094+
2095+
// Please make sure that the function passed to finalization.register()
2096+
// does not create a closure around unnecessary objects.
2097+
function onFinalize(obj, event) {
2098+
// You can do whatever you want with the object
2099+
obj.dispose();
2100+
}
2101+
2102+
finalization.register(myDisposableObject, onFinalize);
2103+
2104+
// Do something
2105+
2106+
myDisposableObject.dispose();
2107+
finalization.unregister(myDisposableObject);
2108+
}
2109+
2110+
setup();
2111+
```
2112+
19002113
## `process.getActiveResourcesInfo()`
19012114
19022115
<!-- YAML

lib/internal/bootstrap/node.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ const rawMethods = internalBinding('process_methods');
182182
process.kill = wrapped.kill;
183183
process.exit = wrapped.exit;
184184

185+
let finalizationMod;
186+
ObjectDefineProperty(process, 'finalization', {
187+
__proto__: null,
188+
get() {
189+
if (finalizationMod !== undefined) {
190+
return finalizationMod;
191+
}
192+
193+
const { createFinalization } = require('internal/process/finalization');
194+
finalizationMod = createFinalization();
195+
196+
return finalizationMod;
197+
},
198+
set(value) {
199+
finalizationMod = value;
200+
},
201+
enumerable: true,
202+
configurable: true,
203+
});
204+
185205
process.hrtime = perThreadSetup.hrtime;
186206
process.hrtime.bigint = perThreadSetup.hrtimeBigInt;
187207

0 commit comments

Comments
 (0)