Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit e4a8d26

Browse files
committed
addon: add AtExit() function
Lets native addons register exit hooks that run after the event loop has quit but before the VM is killed. Fixes #3147.
1 parent 6f82b9f commit e4a8d26

5 files changed

Lines changed: 89 additions & 0 deletions

File tree

‎src/node.cc‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,37 @@ char** Init(int argc, char *argv[]) {
27702770
}
27712771

27722772

2773+
struct AtExitCallback {
2774+
AtExitCallback* next_;
2775+
void (*cb_)(void* arg);
2776+
void* arg_;
2777+
};
2778+
2779+
static AtExitCallback* at_exit_functions_;
2780+
2781+
2782+
void RunAtExit() {
2783+
AtExitCallback* p = at_exit_functions_;
2784+
at_exit_functions_ = NULL;
2785+
2786+
while (p) {
2787+
AtExitCallback* q = p->next_;
2788+
p->cb_(p->arg_);
2789+
delete p;
2790+
p = q;
2791+
}
2792+
}
2793+
2794+
2795+
void AtExit(void (*cb)(void* arg), void* arg) {
2796+
AtExitCallback* p = new AtExitCallback;
2797+
p->cb_ = cb;
2798+
p->arg_ = arg;
2799+
p->next_ = at_exit_functions_;
2800+
at_exit_functions_ = p;
2801+
}
2802+
2803+
27732804
void EmitExit(v8::Handle<v8::Object> process_l) {
27742805
// process.emit('exit')
27752806
process_l->Set(String::NewSymbol("_exiting"), True());
@@ -2850,6 +2881,7 @@ int Start(int argc, char *argv[]) {
28502881
uv_run(uv_default_loop());
28512882

28522883
EmitExit(process_l);
2884+
RunAtExit();
28532885

28542886
#ifndef NDEBUG
28552887
// Clean up.

‎src/node.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ node_module_struct* get_builtin_module(const char *name);
250250
#define NODE_MODULE_DECL(modname) \
251251
extern "C" node::node_module_struct modname ## _module;
252252

253+
/* Called after the event loop exits but before the VM is disposed.
254+
* Callbacks are run in reverse order of registration, i.e. newest first.
255+
*/
256+
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = 0);
257+
253258
NODE_EXTERN void SetErrno(uv_err_t err);
254259
NODE_EXTERN v8::Handle<v8::Value>
255260
MakeCallback(const v8::Handle<v8::Object> object,

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#undef NDEBUG
2+
#include <assert.h>
3+
#include <stdlib.h>
4+
#include <node.h>
5+
#include <v8.h>
6+
7+
using node::AtExit;
8+
using v8::Handle;
9+
using v8::HandleScope;
10+
using v8::Local;
11+
using v8::Object;
12+
13+
static char cookie[] = "yum yum";
14+
static int at_exit_cb1_called = 0;
15+
static int at_exit_cb2_called = 0;
16+
17+
static void at_exit_cb1(void* arg) {
18+
HandleScope scope;
19+
assert(arg == 0);
20+
Local<Object> obj = Object::New();
21+
assert(!obj.IsEmpty()); // assert VM is still alive
22+
assert(obj->IsObject());
23+
at_exit_cb1_called++;
24+
}
25+
26+
static void at_exit_cb2(void* arg) {
27+
assert(arg == static_cast<void*>(cookie));
28+
at_exit_cb2_called++;
29+
}
30+
31+
static void sanity_check(void) {
32+
assert(at_exit_cb1_called == 1);
33+
assert(at_exit_cb2_called == 2);
34+
}
35+
36+
void init(Handle<Object> target) {
37+
AtExit(at_exit_cb1);
38+
AtExit(at_exit_cb2, cookie);
39+
AtExit(at_exit_cb2, cookie);
40+
atexit(sanity_check);
41+
}
42+
43+
NODE_MODULE(binding, init);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.cc' ]
6+
}
7+
]
8+
}

‎test/addons/at-exit/test.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var binding = require('./build/Release/binding');

0 commit comments

Comments
 (0)