src: make AtExit callback's per Environment
This commit attempts to address one of the TODOs in https://github.com/nodejs/node/issues/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: https://github.com/nodejs/node/pull/9163 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
parent
de168b4b4a
commit
ec53921d2e
1
node.gyp
1
node.gyp
@ -642,6 +642,7 @@
|
|||||||
|
|
||||||
'sources': [
|
'sources': [
|
||||||
'test/cctest/test_base64.cc',
|
'test/cctest/test_base64.cc',
|
||||||
|
'test/cctest/test_environment.cc',
|
||||||
'test/cctest/test_util.cc',
|
'test/cctest/test_util.cc',
|
||||||
'test/cctest/test_url.cc'
|
'test/cctest/test_url.cc'
|
||||||
],
|
],
|
||||||
|
11
src/env.cc
11
src/env.cc
@ -153,4 +153,15 @@ void Environment::PrintSyncTrace() const {
|
|||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Environment::RunAtExitCallbacks() {
|
||||||
|
for (AtExitCallback at_exit : at_exit_functions_) {
|
||||||
|
at_exit.cb_(at_exit.arg_);
|
||||||
|
}
|
||||||
|
at_exit_functions_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Environment::AtExit(void (*cb)(void* arg), void* arg) {
|
||||||
|
at_exit_functions_.push_back(AtExitCallback{cb, arg});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
10
src/env.h
10
src/env.h
@ -36,6 +36,7 @@
|
|||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -530,6 +531,9 @@ class Environment {
|
|||||||
|
|
||||||
inline v8::Local<v8::Object> NewInternalFieldObject();
|
inline v8::Local<v8::Object> NewInternalFieldObject();
|
||||||
|
|
||||||
|
void AtExit(void (*cb)(void* arg), void* arg);
|
||||||
|
void RunAtExitCallbacks();
|
||||||
|
|
||||||
// Strings and private symbols are shared across shared contexts
|
// Strings and private symbols are shared across shared contexts
|
||||||
// The getters simply proxy to the per-isolate primitive.
|
// The getters simply proxy to the per-isolate primitive.
|
||||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||||
@ -609,6 +613,12 @@ class Environment {
|
|||||||
|
|
||||||
double* fs_stats_field_array_;
|
double* fs_stats_field_array_;
|
||||||
|
|
||||||
|
struct AtExitCallback {
|
||||||
|
void (*cb_)(void* arg);
|
||||||
|
void* arg_;
|
||||||
|
};
|
||||||
|
std::list<AtExitCallback> at_exit_functions_;
|
||||||
|
|
||||||
#define V(PropertyName, TypeName) \
|
#define V(PropertyName, TypeName) \
|
||||||
v8::Persistent<TypeName> PropertyName ## _;
|
v8::Persistent<TypeName> PropertyName ## _;
|
||||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||||
|
30
src/node.cc
30
src/node.cc
@ -84,7 +84,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||||
#include <unicode/uvernum.h>
|
#include <unicode/uvernum.h>
|
||||||
@ -4255,25 +4254,23 @@ void Init(int* argc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct AtExitCallback {
|
|
||||||
void (*cb_)(void* arg);
|
|
||||||
void* arg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::list<AtExitCallback> at_exit_functions;
|
|
||||||
|
|
||||||
|
|
||||||
// TODO(bnoordhuis) Turn into per-context event.
|
|
||||||
void RunAtExit(Environment* env) {
|
void RunAtExit(Environment* env) {
|
||||||
for (AtExitCallback at_exit : at_exit_functions) {
|
env->RunAtExitCallbacks();
|
||||||
at_exit.cb_(at_exit.arg_);
|
|
||||||
}
|
|
||||||
at_exit_functions.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uv_key_t thread_local_env;
|
||||||
|
|
||||||
|
|
||||||
void AtExit(void (*cb)(void* arg), void* arg) {
|
void AtExit(void (*cb)(void* arg), void* arg) {
|
||||||
at_exit_functions.push_back(AtExitCallback{cb, arg});
|
auto env = static_cast<Environment*>(uv_key_get(&thread_local_env));
|
||||||
|
AtExit(env, cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
|
||||||
|
CHECK_NE(env, nullptr);
|
||||||
|
env->AtExit(cb, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4349,6 +4346,8 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
|
|||||||
Local<Context> context = Context::New(isolate);
|
Local<Context> context = Context::New(isolate);
|
||||||
Context::Scope context_scope(context);
|
Context::Scope context_scope(context);
|
||||||
Environment env(isolate_data, context);
|
Environment env(isolate_data, context);
|
||||||
|
CHECK_EQ(0, uv_key_create(&thread_local_env));
|
||||||
|
uv_key_set(&thread_local_env, &env);
|
||||||
env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
|
env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
|
||||||
|
|
||||||
const char* path = argc > 1 ? argv[1] : nullptr;
|
const char* path = argc > 1 ? argv[1] : nullptr;
|
||||||
@ -4399,6 +4398,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
|
|||||||
|
|
||||||
const int exit_code = EmitExit(&env);
|
const int exit_code = EmitExit(&env);
|
||||||
RunAtExit(&env);
|
RunAtExit(&env);
|
||||||
|
uv_key_delete(&thread_local_env);
|
||||||
|
|
||||||
WaitForInspectorDisconnect(&env);
|
WaitForInspectorDisconnect(&env);
|
||||||
#if defined(LEAK_SANITIZER)
|
#if defined(LEAK_SANITIZER)
|
||||||
|
@ -511,6 +511,12 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
|
|||||||
*/
|
*/
|
||||||
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = 0);
|
NODE_EXTERN void AtExit(void (*cb)(void* arg), void* arg = 0);
|
||||||
|
|
||||||
|
/* Registers a callback with the passed-in Environment instance. The callback
|
||||||
|
* is called after the event loop exits, but before the VM is disposed.
|
||||||
|
* Callbacks are run in reverse order of registration, i.e. newest first.
|
||||||
|
*/
|
||||||
|
NODE_EXTERN void AtExit(Environment* env, void (*cb)(void* arg), void* arg = 0);
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
#endif // SRC_NODE_H_
|
#endif // SRC_NODE_H_
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include "node_test_fixture.h"
|
|
@ -22,7 +22,7 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void* AllocateUninitialized(size_t length) {
|
virtual void* AllocateUninitialized(size_t length) {
|
||||||
return calloc(length, sizeof(int));
|
return calloc(length, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Free(void* data, size_t) {
|
virtual void Free(void* data, size_t) {
|
||||||
@ -35,12 +35,12 @@ struct Argv {
|
|||||||
Argv() : Argv({"node", "-p", "process.version"}) {}
|
Argv() : Argv({"node", "-p", "process.version"}) {}
|
||||||
|
|
||||||
Argv(const std::initializer_list<const char*> &args) {
|
Argv(const std::initializer_list<const char*> &args) {
|
||||||
int nrArgs = args.size();
|
nr_args_ = args.size();
|
||||||
int totalLen = 0;
|
int totalLen = 0;
|
||||||
for (auto it = args.begin(); it != args.end(); ++it) {
|
for (auto it = args.begin(); it != args.end(); ++it) {
|
||||||
totalLen += strlen(*it) + 1;
|
totalLen += strlen(*it) + 1;
|
||||||
}
|
}
|
||||||
argv_ = static_cast<char**>(malloc(nrArgs * sizeof(char*)));
|
argv_ = static_cast<char**>(malloc(nr_args_ * sizeof(char*)));
|
||||||
argv_[0] = static_cast<char*>(malloc(totalLen));
|
argv_[0] = static_cast<char*>(malloc(totalLen));
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
@ -60,12 +60,17 @@ struct Argv {
|
|||||||
free(argv_);
|
free(argv_);
|
||||||
}
|
}
|
||||||
|
|
||||||
char** operator *() const {
|
int nr_args() const {
|
||||||
|
return nr_args_;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** operator*() const {
|
||||||
return argv_;
|
return argv_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char** argv_;
|
char** argv_;
|
||||||
|
int nr_args_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeTestFixture : public ::testing::Test {
|
class NodeTestFixture : public ::testing::Test {
|
||||||
|
112
test/cctest/test_environment.cc
Normal file
112
test/cctest/test_environment.cc
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#include "node.h"
|
||||||
|
#include "env.h"
|
||||||
|
#include "v8.h"
|
||||||
|
#include "libplatform/libplatform.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "node_test_fixture.h"
|
||||||
|
|
||||||
|
using node::Environment;
|
||||||
|
using node::IsolateData;
|
||||||
|
using node::CreateIsolateData;
|
||||||
|
using node::FreeIsolateData;
|
||||||
|
using node::CreateEnvironment;
|
||||||
|
using node::FreeEnvironment;
|
||||||
|
using node::AtExit;
|
||||||
|
using node::RunAtExit;
|
||||||
|
|
||||||
|
static bool called_cb_1 = false;
|
||||||
|
static bool called_cb_2 = false;
|
||||||
|
static void at_exit_callback1(void* arg);
|
||||||
|
static void at_exit_callback2(void* arg);
|
||||||
|
static std::string cb_1_arg; // NOLINT(runtime/string)
|
||||||
|
|
||||||
|
class EnvironmentTest : public NodeTestFixture {
|
||||||
|
public:
|
||||||
|
class Env {
|
||||||
|
public:
|
||||||
|
Env(const v8::HandleScope& handle_scope,
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
const Argv& argv) {
|
||||||
|
context_ = v8::Context::New(isolate);
|
||||||
|
CHECK(!context_.IsEmpty());
|
||||||
|
isolate_data_ = CreateIsolateData(isolate, uv_default_loop());
|
||||||
|
CHECK_NE(nullptr, isolate_data_);
|
||||||
|
environment_ = CreateEnvironment(isolate_data_,
|
||||||
|
context_,
|
||||||
|
1, *argv,
|
||||||
|
argv.nr_args(), *argv);
|
||||||
|
CHECK_NE(nullptr, environment_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Env() {
|
||||||
|
FreeIsolateData(isolate_data_);
|
||||||
|
FreeEnvironment(environment_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment* operator*() const {
|
||||||
|
return environment_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
v8::Local<v8::Context> context_;
|
||||||
|
IsolateData* isolate_data_;
|
||||||
|
Environment* environment_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void TearDown() {
|
||||||
|
NodeTestFixture::TearDown();
|
||||||
|
called_cb_1 = false;
|
||||||
|
called_cb_2 = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(EnvironmentTest, AtExitWithEnvironment) {
|
||||||
|
const v8::HandleScope handle_scope(isolate_);
|
||||||
|
const Argv argv;
|
||||||
|
Env env {handle_scope, isolate_, argv};
|
||||||
|
|
||||||
|
AtExit(*env, at_exit_callback1);
|
||||||
|
RunAtExit(*env);
|
||||||
|
EXPECT_TRUE(called_cb_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EnvironmentTest, AtExitWithArgument) {
|
||||||
|
const v8::HandleScope handle_scope(isolate_);
|
||||||
|
const Argv argv;
|
||||||
|
Env env {handle_scope, isolate_, argv};
|
||||||
|
|
||||||
|
std::string arg{"some args"};
|
||||||
|
AtExit(*env, at_exit_callback1, static_cast<void*>(&arg));
|
||||||
|
RunAtExit(*env);
|
||||||
|
EXPECT_EQ(arg, cb_1_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
|
||||||
|
const v8::HandleScope handle_scope(isolate_);
|
||||||
|
const Argv argv;
|
||||||
|
Env env1 {handle_scope, isolate_, argv};
|
||||||
|
Env env2 {handle_scope, isolate_, argv};
|
||||||
|
|
||||||
|
AtExit(*env1, at_exit_callback1);
|
||||||
|
AtExit(*env2, at_exit_callback2);
|
||||||
|
RunAtExit(*env1);
|
||||||
|
EXPECT_TRUE(called_cb_1);
|
||||||
|
EXPECT_FALSE(called_cb_2);
|
||||||
|
|
||||||
|
RunAtExit(*env2);
|
||||||
|
EXPECT_TRUE(called_cb_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void at_exit_callback1(void* arg) {
|
||||||
|
called_cb_1 = true;
|
||||||
|
if (arg) {
|
||||||
|
cb_1_arg = *static_cast<std::string*>(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void at_exit_callback2(void* arg) {
|
||||||
|
called_cb_2 = true;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user