forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfinalization.js
More file actions
150 lines (125 loc) Β· 3.3 KB
/
finalization.js
File metadata and controls
150 lines (125 loc) Β· 3.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
'use strict';
// This file is a modified version of the on-exit-leak-free module on npm.
const {
ArrayPrototypeFilter,
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypeSplice,
SafeFinalizationRegistry,
SafeWeakRef,
} = primordials;
const { validateObject, kValidateObjectAllowFunction } = require('internal/validators');
const { emitExperimentalWarning } = require('internal/util');
function createFinalization() {
/**
* @type {SafeFinalizationRegistry}
*/
let registry = null;
const refs = {
__proto__: null,
exit: [],
beforeExit: [],
};
const functions = {
__proto__: null,
exit: onExit,
beforeExit: onBeforeExit,
};
function install(event) {
if (refs[event].length > 0) {
return;
}
process.on(event, functions[event]);
}
function uninstall(event) {
if (refs[event].length > 0) {
return;
}
process.removeListener(event, functions[event]);
if (refs.exit.length === 0 && refs.beforeExit.length === 0) {
registry = null;
}
}
function onExit() {
callRefsToFree('exit');
}
function onBeforeExit() {
callRefsToFree('beforeExit');
}
function callRefsToFree(event) {
for (const ref of refs[event]) {
const obj = ref.deref();
const fn = ref.fn;
// This should always happen, however GC is
// indeterministic so it might not happen.
/* istanbul ignore else */
if (obj !== undefined) {
fn(obj, event);
}
}
refs[event] = [];
}
function clear(ref) {
for (const event of ['exit', 'beforeExit']) {
const index = ArrayPrototypeIndexOf(refs[event], ref);
ArrayPrototypeSplice(refs[event], index, index + 1);
uninstall(event);
}
}
function _register(event, obj, fn) {
install(event);
const ref = new SafeWeakRef(obj);
ref.fn = fn;
registry ||= new SafeFinalizationRegistry(clear);
registry.register(obj, ref);
ArrayPrototypePush(refs[event], ref);
}
/**
* Execute the given function when the process exits,
* and clean things up when the object is gc.
* @param {any} obj
* @param {Function} fn
*/
function register(obj, fn) {
emitExperimentalWarning('process.finalization.register');
validateObject(obj, 'obj', kValidateObjectAllowFunction);
_register('exit', obj, fn);
}
/**
* Execute the given function before the process exits,
* and clean things up when the object is gc.
* @param {any} obj
* @param {Function} fn
*/
function registerBeforeExit(obj, fn) {
emitExperimentalWarning('process.finalization.registerBeforeExit');
validateObject(obj, 'obj', kValidateObjectAllowFunction);
_register('beforeExit', obj, fn);
}
/**
* Unregister the given object from the onExit or onBeforeExit event.
* @param {object} obj
*/
function unregister(obj) {
emitExperimentalWarning('process.finalization.unregister');
if (!registry) {
return;
}
registry.unregister(obj);
for (const event of ['exit', 'beforeExit']) {
refs[event] = ArrayPrototypeFilter(refs[event], (ref) => {
const _obj = ref.deref();
return _obj && _obj !== obj;
});
uninstall(event);
}
}
return {
register,
registerBeforeExit,
unregister,
};
}
module.exports = {
createFinalization,
};