Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0beb8a6

Browse filesBrowse files
addaleaxMylesBorins
authored andcommitted
src: deprecate two- and one-argument AtExit()
Using `AtExit()` without an `Environment*` pointer or providing an argument is almost always a sign of improperly relying on global state and/or using `AtExit()` as an addon when the addon-targeting `AddEnvironmentCleanupHook()` would be the better choice. Deprecate those variants. This also updates the addon docs to refer to `AddEnvironmentCleanupHook()` rather than `AtExit()`. PR-URL: #30227 Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent bf4c39d commit 0beb8a6
Copy full SHA for 0beb8a6

File tree

Expand file treeCollapse file tree

3 files changed

+81
-89
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+81
-89
lines changed
Open diff view settings
Collapse file

‎doc/api/addons.md‎

Copy file name to clipboardExpand all lines: doc/api/addons.md
+60-84Lines changed: 60 additions & 84 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,12 @@ NODE_MODULE_INIT(/* exports, module, context */) {
241241

242242
#### Worker support
243243

244+
In order to be loaded from multiple Node.js environments,
245+
such as a main thread and a Worker thread, an add-on needs to either:
246+
247+
* Be an N-API addon, or
248+
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above
249+
244250
In order to support [`Worker`][] threads, addons need to clean up any resources
245251
they may have allocated when such a thread exists. This can be achieved through
246252
the usage of the `AddEnvironmentCleanupHook()` function:
@@ -254,13 +260,62 @@ void AddEnvironmentCleanupHook(v8::Isolate* isolate,
254260
This function adds a hook that will run before a given Node.js instance shuts
255261
down. If necessary, such hooks can be removed using
256262
`RemoveEnvironmentCleanupHook()` before they are run, which has the same
257-
signature.
263+
signature. Callbacks are run in last-in first-out order.
258264
259-
In order to be loaded from multiple Node.js environments,
260-
such as a main thread and a Worker thread, an add-on needs to either:
265+
The following `addon.cc` uses `AddEnvironmentCleanupHook`:
261266
262-
* Be an N-API addon, or
263-
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above
267+
```cpp
268+
// addon.cc
269+
#include <assert.h>
270+
#include <stdlib.h>
271+
#include <node.h>
272+
273+
using node::AddEnvironmentCleanupHook;
274+
using v8::HandleScope;
275+
using v8::Isolate;
276+
using v8::Local;
277+
using v8::Object;
278+
279+
// Note: In a real-world application, do not rely on static/global data.
280+
static char cookie[] = "yum yum";
281+
static int cleanup_cb1_called = 0;
282+
static int cleanup_cb2_called = 0;
283+
284+
static void cleanup_cb1(void* arg) {
285+
Isolate* isolate = static_cast<Isolate*>(arg);
286+
HandleScope scope(isolate);
287+
Local<Object> obj = Object::New(isolate);
288+
assert(!obj.IsEmpty()); // assert VM is still alive
289+
assert(obj->IsObject());
290+
cleanup_cb1_called++;
291+
}
292+
293+
static void cleanup_cb2(void* arg) {
294+
assert(arg == static_cast<void*>(cookie));
295+
cleanup_cb2_called++;
296+
}
297+
298+
static void sanity_check(void*) {
299+
assert(cleanup_cb1_called == 1);
300+
assert(cleanup_cb2_called == 1);
301+
}
302+
303+
// Initialize this addon to be context-aware.
304+
NODE_MODULE_INIT(/* exports, module, context */) {
305+
Isolate* isolate = context->GetIsolate();
306+
307+
AddEnvironmentCleanupHook(isolate, sanity_check, nullptr);
308+
AddEnvironmentCleanupHook(isolate, cleanup_cb2, cookie);
309+
AddEnvironmentCleanupHook(isolate, cleanup_cb1, isolate);
310+
}
311+
```
312+
313+
Test in JavaScript by running:
314+
315+
```js
316+
// test.js
317+
require('./build/Release/addon');
318+
```
264319

265320
### Building
266321

@@ -1293,85 +1348,6 @@ console.log(result);
12931348
// Prints: 30
12941349
```
12951350

1296-
### AtExit hooks
1297-
1298-
An `AtExit` hook is a function that is invoked after the Node.js event loop
1299-
has ended but before the JavaScript VM is terminated and Node.js shuts down.
1300-
`AtExit` hooks are registered using the `node::AtExit` API.
1301-
1302-
#### void AtExit(callback, args)
1303-
1304-
* `callback` <span class="type">&lt;void (\*)(void\*)&gt;</span>
1305-
A pointer to the function to call at exit.
1306-
* `args` <span class="type">&lt;void\*&gt;</span>
1307-
A pointer to pass to the callback at exit.
1308-
1309-
Registers exit hooks that run after the event loop has ended but before the VM
1310-
is killed.
1311-
1312-
`AtExit` takes two parameters: a pointer to a callback function to run at exit,
1313-
and a pointer to untyped context data to be passed to that callback.
1314-
1315-
Callbacks are run in last-in first-out order.
1316-
1317-
The following `addon.cc` implements `AtExit`:
1318-
1319-
```cpp
1320-
// addon.cc
1321-
#include <assert.h>
1322-
#include <stdlib.h>
1323-
#include <node.h>
1324-
1325-
namespace demo {
1326-
1327-
using node::AtExit;
1328-
using v8::HandleScope;
1329-
using v8::Isolate;
1330-
using v8::Local;
1331-
using v8::Object;
1332-
1333-
static char cookie[] = "yum yum";
1334-
static int at_exit_cb1_called = 0;
1335-
static int at_exit_cb2_called = 0;
1336-
1337-
static void at_exit_cb1(void* arg) {
1338-
Isolate* isolate = static_cast<Isolate*>(arg);
1339-
HandleScope scope(isolate);
1340-
Local<Object> obj = Object::New(isolate);
1341-
assert(!obj.IsEmpty()); // assert VM is still alive
1342-
assert(obj->IsObject());
1343-
at_exit_cb1_called++;
1344-
}
1345-
1346-
static void at_exit_cb2(void* arg) {
1347-
assert(arg == static_cast<void*>(cookie));
1348-
at_exit_cb2_called++;
1349-
}
1350-
1351-
static void sanity_check(void*) {
1352-
assert(at_exit_cb1_called == 1);
1353-
assert(at_exit_cb2_called == 2);
1354-
}
1355-
1356-
void init(Local<Object> exports) {
1357-
AtExit(sanity_check);
1358-
AtExit(at_exit_cb2, cookie);
1359-
AtExit(at_exit_cb2, cookie);
1360-
AtExit(at_exit_cb1, exports->GetIsolate());
1361-
}
1362-
1363-
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
1364-
1365-
} // namespace demo
1366-
```
1367-
1368-
Test in JavaScript by running:
1369-
1370-
```js
1371-
// test.js
1372-
require('./build/Release/addon');
1373-
```
1374-
13751351
[`Worker`]: worker_threads.html#worker_threads_class_worker
13761352
[Electron]: https://electronjs.org/
13771353
[Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide
Collapse file

‎src/node.h‎

Copy file name to clipboardExpand all lines: src/node.h
+13-2Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,16 +664,27 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
664664

665665
/* Called after the event loop exits but before the VM is disposed.
666666
* Callbacks are run in reverse order of registration, i.e. newest first.
667+
*
668+
* You should always use the three-argument variant (or, for addons,
669+
* AddEnvironmentCleanupHook) in order to avoid relying on global state.
667670
*/
668-
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = nullptr);
671+
NODE_DEPRECATED(
672+
"Use the three-argument variant of AtExit() or AddEnvironmentCleanupHook()",
673+
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = nullptr));
669674

670675
/* Registers a callback with the passed-in Environment instance. The callback
671676
* is called after the event loop exits, but before the VM is disposed.
672677
* Callbacks are run in reverse order of registration, i.e. newest first.
673678
*/
674679
NODE_EXTERN void AtExit(Environment* env,
675680
void (*cb)(void* arg),
676-
void* arg = nullptr);
681+
void* arg);
682+
NODE_DEPRECATED(
683+
"Use the three-argument variant of AtExit() or AddEnvironmentCleanupHook()",
684+
inline void AtExit(Environment* env,
685+
void (*cb)(void* arg)) {
686+
AtExit(env, cb, nullptr);
687+
})
677688

678689
typedef double async_id;
679690
struct async_context {
Collapse file

‎test/addons/at-exit/binding.cc‎

Copy file name to clipboardExpand all lines: test/addons/at-exit/binding.cc
+8-3Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
#include <node.h>
44
#include <v8.h>
55

6+
// TODO(addaleax): Maybe merge this file with the cctest for AtExit()?
7+
68
using node::AtExit;
9+
using node::Environment;
10+
using node::GetCurrentEnvironment;
711
using v8::HandleScope;
812
using v8::Isolate;
913
using v8::Local;
@@ -46,9 +50,10 @@ NODE_C_DTOR(sanity_check) {
4650
}
4751

4852
void init(Local<Object> exports) {
49-
AtExit(at_exit_cb1, exports->GetIsolate());
50-
AtExit(at_exit_cb2, cookie);
51-
AtExit(at_exit_cb2, cookie);
53+
Environment* env = GetCurrentEnvironment(exports->CreationContext());
54+
AtExit(env, at_exit_cb1, exports->GetIsolate());
55+
AtExit(env, at_exit_cb2, cookie);
56+
AtExit(env, at_exit_cb2, cookie);
5257
}
5358

5459
NODE_MODULE(NODE_GYP_MODULE_NAME, init)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.