Skip to content

Commit ec53921

Browse files
committed
src: make AtExit callback's per Environment
This commit attempts to address one of the TODOs in #4641 regarding making the AtExit callback's per environment, instead of the current global. bnoordhuis provided a few options for solving this, and one was to use a thread-local which is what this commit attempts to do. PR-URL: #9163 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
1 parent de168b4 commit ec53921

8 files changed

+163
-20
lines changed

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@
642642

643643
'sources': [
644644
'test/cctest/test_base64.cc',
645+
'test/cctest/test_environment.cc',
645646
'test/cctest/test_util.cc',
646647
'test/cctest/test_url.cc'
647648
],

src/env.cc

+11
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,15 @@ void Environment::PrintSyncTrace() const {
153153
fflush(stderr);
154154
}
155155

156+
void Environment::RunAtExitCallbacks() {
157+
for (AtExitCallback at_exit : at_exit_functions_) {
158+
at_exit.cb_(at_exit.arg_);
159+
}
160+
at_exit_functions_.clear();
161+
}
162+
163+
void Environment::AtExit(void (*cb)(void* arg), void* arg) {
164+
at_exit_functions_.push_back(AtExitCallback{cb, arg});
165+
}
166+
156167
} // namespace node

src/env.h

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "uv.h"
3737
#include "v8.h"
3838

39+
#include <list>
3940
#include <stdint.h>
4041
#include <vector>
4142

@@ -530,6 +531,9 @@ class Environment {
530531

531532
inline v8::Local<v8::Object> NewInternalFieldObject();
532533

534+
void AtExit(void (*cb)(void* arg), void* arg);
535+
void RunAtExitCallbacks();
536+
533537
// Strings and private symbols are shared across shared contexts
534538
// The getters simply proxy to the per-isolate primitive.
535539
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
@@ -609,6 +613,12 @@ class Environment {
609613

610614
double* fs_stats_field_array_;
611615

616+
struct AtExitCallback {
617+
void (*cb_)(void* arg);
618+
void* arg_;
619+
};
620+
std::list<AtExitCallback> at_exit_functions_;
621+
612622
#define V(PropertyName, TypeName) \
613623
v8::Persistent<TypeName> PropertyName ## _;
614624
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)

src/node.cc

+14-14
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@
8484

8585
#include <string>
8686
#include <vector>
87-
#include <list>
8887

8988
#if defined(NODE_HAVE_I18N_SUPPORT)
9089
#include <unicode/uvernum.h>
@@ -4255,25 +4254,23 @@ void Init(int* argc,
42554254
}
42564255

42574256

4258-
struct AtExitCallback {
4259-
void (*cb_)(void* arg);
4260-
void* arg_;
4261-
};
4257+
void RunAtExit(Environment* env) {
4258+
env->RunAtExitCallbacks();
4259+
}
42624260

4263-
static std::list<AtExitCallback> at_exit_functions;
42644261

4262+
static uv_key_t thread_local_env;
42654263

4266-
// TODO(bnoordhuis) Turn into per-context event.
4267-
void RunAtExit(Environment* env) {
4268-
for (AtExitCallback at_exit : at_exit_functions) {
4269-
at_exit.cb_(at_exit.arg_);
4270-
}
4271-
at_exit_functions.clear();
4264+
4265+
void AtExit(void (*cb)(void* arg), void* arg) {
4266+
auto env = static_cast<Environment*>(uv_key_get(&thread_local_env));
4267+
AtExit(env, cb, arg);
42724268
}
42734269

42744270

4275-
void AtExit(void (*cb)(void* arg), void* arg) {
4276-
at_exit_functions.push_back(AtExitCallback{cb, arg});
4271+
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
4272+
CHECK_NE(env, nullptr);
4273+
env->AtExit(cb, arg);
42774274
}
42784275

42794276

@@ -4349,6 +4346,8 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
43494346
Local<Context> context = Context::New(isolate);
43504347
Context::Scope context_scope(context);
43514348
Environment env(isolate_data, context);
4349+
CHECK_EQ(0, uv_key_create(&thread_local_env));
4350+
uv_key_set(&thread_local_env, &env);
43524351
env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
43534352

43544353
const char* path = argc > 1 ? argv[1] : nullptr;
@@ -4399,6 +4398,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
43994398

44004399
const int exit_code = EmitExit(&env);
44014400
RunAtExit(&env);
4401+
uv_key_delete(&thread_local_env);
44024402

44034403
WaitForInspectorDisconnect(&env);
44044404
#if defined(LEAK_SANITIZER)

src/node.h

+6
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,12 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
511511
*/
512512
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = 0);
513513

514+
/* Registers a callback with the passed-in Environment instance. The callback
515+
* is called after the event loop exits, but before the VM is disposed.
516+
* Callbacks are run in reverse order of registration, i.e. newest first.
517+
*/
518+
NODE_EXTERN void AtExit(Environment* env, void (*cb)(void* arg), void* arg = 0);
519+
514520
} // namespace node
515521

516522
#endif // SRC_NODE_H_

test/cctest/node_test_fixture.cc

-2
This file was deleted.

test/cctest/node_test_fixture.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
2222
}
2323

2424
virtual void* AllocateUninitialized(size_t length) {
25-
return calloc(length, sizeof(int));
25+
return calloc(length, 1);
2626
}
2727

2828
virtual void Free(void* data, size_t) {
@@ -35,12 +35,12 @@ struct Argv {
3535
Argv() : Argv({"node", "-p", "process.version"}) {}
3636

3737
Argv(const std::initializer_list<const char*> &args) {
38-
int nrArgs = args.size();
38+
nr_args_ = args.size();
3939
int totalLen = 0;
4040
for (auto it = args.begin(); it != args.end(); ++it) {
4141
totalLen += strlen(*it) + 1;
4242
}
43-
argv_ = static_cast<char**>(malloc(nrArgs * sizeof(char*)));
43+
argv_ = static_cast<char**>(malloc(nr_args_ * sizeof(char*)));
4444
argv_[0] = static_cast<char*>(malloc(totalLen));
4545
int i = 0;
4646
int offset = 0;
@@ -60,12 +60,17 @@ struct Argv {
6060
free(argv_);
6161
}
6262

63-
char** operator *() const {
63+
int nr_args() const {
64+
return nr_args_;
65+
}
66+
67+
char** operator*() const {
6468
return argv_;
6569
}
6670

6771
private:
6872
char** argv_;
73+
int nr_args_;
6974
};
7075

7176
class NodeTestFixture : public ::testing::Test {

test/cctest/test_environment.cc

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include "node.h"
2+
#include "env.h"
3+
#include "v8.h"
4+
#include "libplatform/libplatform.h"
5+
6+
#include <string>
7+
#include "gtest/gtest.h"
8+
#include "node_test_fixture.h"
9+
10+
using node::Environment;
11+
using node::IsolateData;
12+
using node::CreateIsolateData;
13+
using node::FreeIsolateData;
14+
using node::CreateEnvironment;
15+
using node::FreeEnvironment;
16+
using node::AtExit;
17+
using node::RunAtExit;
18+
19+
static bool called_cb_1 = false;
20+
static bool called_cb_2 = false;
21+
static void at_exit_callback1(void* arg);
22+
static void at_exit_callback2(void* arg);
23+
static std::string cb_1_arg; // NOLINT(runtime/string)
24+
25+
class EnvironmentTest : public NodeTestFixture {
26+
public:
27+
class Env {
28+
public:
29+
Env(const v8::HandleScope& handle_scope,
30+
v8::Isolate* isolate,
31+
const Argv& argv) {
32+
context_ = v8::Context::New(isolate);
33+
CHECK(!context_.IsEmpty());
34+
isolate_data_ = CreateIsolateData(isolate, uv_default_loop());
35+
CHECK_NE(nullptr, isolate_data_);
36+
environment_ = CreateEnvironment(isolate_data_,
37+
context_,
38+
1, *argv,
39+
argv.nr_args(), *argv);
40+
CHECK_NE(nullptr, environment_);
41+
}
42+
43+
~Env() {
44+
FreeIsolateData(isolate_data_);
45+
FreeEnvironment(environment_);
46+
}
47+
48+
Environment* operator*() const {
49+
return environment_;
50+
}
51+
52+
private:
53+
v8::Local<v8::Context> context_;
54+
IsolateData* isolate_data_;
55+
Environment* environment_;
56+
};
57+
58+
private:
59+
virtual void TearDown() {
60+
NodeTestFixture::TearDown();
61+
called_cb_1 = false;
62+
called_cb_2 = false;
63+
}
64+
};
65+
66+
TEST_F(EnvironmentTest, AtExitWithEnvironment) {
67+
const v8::HandleScope handle_scope(isolate_);
68+
const Argv argv;
69+
Env env {handle_scope, isolate_, argv};
70+
71+
AtExit(*env, at_exit_callback1);
72+
RunAtExit(*env);
73+
EXPECT_TRUE(called_cb_1);
74+
}
75+
76+
TEST_F(EnvironmentTest, AtExitWithArgument) {
77+
const v8::HandleScope handle_scope(isolate_);
78+
const Argv argv;
79+
Env env {handle_scope, isolate_, argv};
80+
81+
std::string arg{"some args"};
82+
AtExit(*env, at_exit_callback1, static_cast<void*>(&arg));
83+
RunAtExit(*env);
84+
EXPECT_EQ(arg, cb_1_arg);
85+
}
86+
87+
TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
88+
const v8::HandleScope handle_scope(isolate_);
89+
const Argv argv;
90+
Env env1 {handle_scope, isolate_, argv};
91+
Env env2 {handle_scope, isolate_, argv};
92+
93+
AtExit(*env1, at_exit_callback1);
94+
AtExit(*env2, at_exit_callback2);
95+
RunAtExit(*env1);
96+
EXPECT_TRUE(called_cb_1);
97+
EXPECT_FALSE(called_cb_2);
98+
99+
RunAtExit(*env2);
100+
EXPECT_TRUE(called_cb_2);
101+
}
102+
103+
static void at_exit_callback1(void* arg) {
104+
called_cb_1 = true;
105+
if (arg) {
106+
cb_1_arg = *static_cast<std::string*>(arg);
107+
}
108+
}
109+
110+
static void at_exit_callback2(void* arg) {
111+
called_cb_2 = true;
112+
}

0 commit comments

Comments
 (0)