Skip to content

Commit cab1dc5

Browse files
committedApr 16, 2019
src: use RAII to manage the main isolate data
This patch encapsulates the main isolate management into a NodeMainInstance class that manages the resources with RAII and controls the Isolate::CreateParams (which is necessary for deserializing snapshots with external references) PR-URL: #27220 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent c6c37e9 commit cab1dc5

7 files changed

+236
-184
lines changed
 

‎node.gyp

+2
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@
460460
'src/node_http_parser_traditional.cc',
461461
'src/node_http2.cc',
462462
'src/node_i18n.cc',
463+
'src/node_main_instance.cc',
463464
'src/node_messaging.cc',
464465
'src/node_metadata.cc',
465466
'src/node_native_module.cc',
@@ -540,6 +541,7 @@
540541
'src/node_http2_state.h',
541542
'src/node_i18n.h',
542543
'src/node_internals.h',
544+
'src/node_main_instance.h',
543545
'src/node_messaging.h',
544546
'src/node_metadata.h',
545547
'src/node_mutex.h',

‎src/api/environment.cc

+14-10
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,7 @@ void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {
169169
delete allocator;
170170
}
171171

172-
void SetIsolateCreateParams(Isolate::CreateParams* params,
173-
ArrayBufferAllocator* allocator) {
174-
if (allocator != nullptr)
175-
params->array_buffer_allocator = allocator;
176-
172+
void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
177173
const uint64_t total_memory = uv_get_total_memory();
178174
if (total_memory > 0) {
179175
// V8 defaults to 700MB or 1.4GB on 32 and 64 bit platforms respectively.
@@ -204,25 +200,33 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop) {
204200
return NewIsolate(allocator, event_loop, GetMainThreadMultiIsolatePlatform());
205201
}
206202

207-
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
203+
// TODO(joyeecheung): we may want to expose this, but then we need to be
204+
// careful about what we override in the params.
205+
Isolate* NewIsolate(Isolate::CreateParams* params,
208206
uv_loop_t* event_loop,
209207
MultiIsolatePlatform* platform) {
210-
Isolate::CreateParams params;
211-
SetIsolateCreateParams(&params, allocator);
212-
213208
Isolate* isolate = Isolate::Allocate();
214209
if (isolate == nullptr) return nullptr;
215210

216211
// Register the isolate on the platform before the isolate gets initialized,
217212
// so that the isolate can access the platform during initialization.
218213
platform->RegisterIsolate(isolate, event_loop);
219-
Isolate::Initialize(isolate, params);
220214

215+
SetIsolateCreateParamsForNode(params);
216+
Isolate::Initialize(isolate, *params);
221217
SetIsolateUpForNode(isolate);
222218

223219
return isolate;
224220
}
225221

222+
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
223+
uv_loop_t* event_loop,
224+
MultiIsolatePlatform* platform) {
225+
Isolate::CreateParams params;
226+
if (allocator != nullptr) params.array_buffer_allocator = allocator;
227+
return NewIsolate(&params, event_loop, platform);
228+
}
229+
226230
IsolateData* CreateIsolateData(Isolate* isolate,
227231
uv_loop_t* loop,
228232
MultiIsolatePlatform* platform,

‎src/env.cc

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ IsolateData::IsolateData(Isolate* isolate,
7676
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
7777
// decoding step.
7878

79+
HandleScope handle_scope(isolate);
80+
7981
#define V(PropertyName, StringValue) \
8082
PropertyName ## _.Set( \
8183
isolate, \

‎src/node.cc

+8-173
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,12 @@
2525

2626
#include "debug_utils.h"
2727
#include "node_binding.h"
28-
#include "node_buffer.h"
29-
#include "node_constants.h"
30-
#include "node_context_data.h"
31-
#include "node_errors.h"
3228
#include "node_internals.h"
29+
#include "node_main_instance.h"
3330
#include "node_metadata.h"
3431
#include "node_native_module_env.h"
3532
#include "node_options-inl.h"
3633
#include "node_perf.h"
37-
#include "node_platform.h"
3834
#include "node_process.h"
3935
#include "node_revert.h"
4036
#include "node_v8_platform-inl.h"
@@ -56,13 +52,6 @@
5652
#include "node_dtrace.h"
5753
#endif
5854

59-
#include "async_wrap-inl.h"
60-
#include "env-inl.h"
61-
#include "handle_wrap.h"
62-
#include "req_wrap-inl.h"
63-
#include "string_bytes.h"
64-
#include "util.h"
65-
#include "uv.h"
6655
#if NODE_USE_V8_PLATFORM
6756
#include "libplatform/libplatform.h"
6857
#endif // NODE_USE_V8_PLATFORM
@@ -122,23 +111,19 @@ using native_module::NativeModuleEnv;
122111
using options_parser::kAllowedInEnvironment;
123112
using options_parser::kDisallowedInEnvironment;
124113

125-
using v8::Array;
126114
using v8::Boolean;
127115
using v8::Context;
128-
using v8::DEFAULT;
129116
using v8::EscapableHandleScope;
130117
using v8::Exception;
131118
using v8::Function;
132119
using v8::FunctionCallbackInfo;
133120
using v8::HandleScope;
134121
using v8::Isolate;
135122
using v8::Local;
136-
using v8::Locker;
137123
using v8::Maybe;
138124
using v8::MaybeLocal;
139125
using v8::Object;
140126
using v8::Script;
141-
using v8::SealHandleScope;
142127
using v8::String;
143128
using v8::Undefined;
144129
using v8::V8;
@@ -767,161 +752,6 @@ void Init(int* argc,
767752
argv[i] = strdup(argv_[i].c_str());
768753
}
769754

770-
void RunBeforeExit(Environment* env) {
771-
env->RunBeforeExitCallbacks();
772-
773-
if (!uv_loop_alive(env->event_loop()))
774-
EmitBeforeExit(env);
775-
}
776-
777-
// TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h
778-
// and the environment creation routine in workers somehow.
779-
inline std::unique_ptr<Environment> CreateMainEnvironment(
780-
IsolateData* isolate_data,
781-
const std::vector<std::string>& args,
782-
const std::vector<std::string>& exec_args,
783-
int* exit_code) {
784-
Isolate* isolate = isolate_data->isolate();
785-
HandleScope handle_scope(isolate);
786-
787-
// TODO(addaleax): This should load a real per-Isolate option, currently
788-
// this is still effectively per-process.
789-
if (isolate_data->options()->track_heap_objects) {
790-
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
791-
}
792-
793-
Local<Context> context = NewContext(isolate);
794-
Context::Scope context_scope(context);
795-
796-
std::unique_ptr<Environment> env = std::make_unique<Environment>(
797-
isolate_data,
798-
context,
799-
static_cast<Environment::Flags>(Environment::kIsMainThread |
800-
Environment::kOwnsProcessState |
801-
Environment::kOwnsInspector));
802-
env->InitializeLibuv(per_process::v8_is_profiling);
803-
env->ProcessCliArgs(args, exec_args);
804-
805-
#if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM
806-
CHECK(!env->inspector_agent()->IsListening());
807-
// Inspector agent can't fail to start, but if it was configured to listen
808-
// right away on the websocket port and fails to bind/etc, this will return
809-
// false.
810-
env->inspector_agent()->Start(args.size() > 1 ? args[1].c_str() : "",
811-
env->options()->debug_options(),
812-
env->inspector_host_port(),
813-
true);
814-
if (env->options()->debug_options().inspector_enabled &&
815-
!env->inspector_agent()->IsListening()) {
816-
*exit_code = 12; // Signal internal error.
817-
return env;
818-
}
819-
#else
820-
// inspector_enabled can't be true if !HAVE_INSPECTOR or !NODE_USE_V8_PLATFORM
821-
// - the option parser should not allow that.
822-
CHECK(!env->options()->debug_options().inspector_enabled);
823-
#endif // HAVE_INSPECTOR && NODE_USE_V8_PLATFORM
824-
825-
if (RunBootstrapping(env.get()).IsEmpty()) {
826-
*exit_code = 1;
827-
}
828-
829-
return env;
830-
}
831-
832-
inline int StartNodeWithIsolate(Isolate* isolate,
833-
IsolateData* isolate_data,
834-
const std::vector<std::string>& args,
835-
const std::vector<std::string>& exec_args) {
836-
int exit_code = 0;
837-
std::unique_ptr<Environment> env =
838-
CreateMainEnvironment(isolate_data, args, exec_args, &exit_code);
839-
CHECK_NOT_NULL(env);
840-
HandleScope handle_scope(env->isolate());
841-
Context::Scope context_scope(env->context());
842-
843-
if (exit_code == 0) {
844-
{
845-
AsyncCallbackScope callback_scope(env.get());
846-
env->async_hooks()->push_async_ids(1, 0);
847-
LoadEnvironment(env.get());
848-
env->async_hooks()->pop_async_id(1);
849-
}
850-
851-
{
852-
SealHandleScope seal(isolate);
853-
bool more;
854-
env->performance_state()->Mark(
855-
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
856-
do {
857-
uv_run(env->event_loop(), UV_RUN_DEFAULT);
858-
859-
per_process::v8_platform.DrainVMTasks(isolate);
860-
861-
more = uv_loop_alive(env->event_loop());
862-
if (more && !env->is_stopping()) continue;
863-
864-
RunBeforeExit(env.get());
865-
866-
// Emit `beforeExit` if the loop became alive either after emitting
867-
// event, or after running some callbacks.
868-
more = uv_loop_alive(env->event_loop());
869-
} while (more == true && !env->is_stopping());
870-
env->performance_state()->Mark(
871-
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
872-
}
873-
874-
env->set_trace_sync_io(false);
875-
exit_code = EmitExit(env.get());
876-
WaitForInspectorDisconnect(env.get());
877-
}
878-
879-
env->set_can_call_into_js(false);
880-
env->stop_sub_worker_contexts();
881-
uv_tty_reset_mode();
882-
env->RunCleanup();
883-
RunAtExit(env.get());
884-
885-
per_process::v8_platform.DrainVMTasks(isolate);
886-
per_process::v8_platform.CancelVMTasks(isolate);
887-
888-
#if defined(LEAK_SANITIZER)
889-
__lsan_do_leak_check();
890-
#endif
891-
892-
return exit_code;
893-
}
894-
895-
inline int StartNodeWithLoopAndArgs(uv_loop_t* event_loop,
896-
const std::vector<std::string>& args,
897-
const std::vector<std::string>& exec_args) {
898-
std::unique_ptr<ArrayBufferAllocator, decltype(&FreeArrayBufferAllocator)>
899-
allocator(CreateArrayBufferAllocator(), &FreeArrayBufferAllocator);
900-
Isolate* const isolate = NewIsolate(allocator.get(), event_loop);
901-
if (isolate == nullptr)
902-
return 12; // Signal internal error.
903-
904-
int exit_code;
905-
{
906-
Locker locker(isolate);
907-
Isolate::Scope isolate_scope(isolate);
908-
HandleScope handle_scope(isolate);
909-
std::unique_ptr<IsolateData, decltype(&FreeIsolateData)> isolate_data(
910-
CreateIsolateData(isolate,
911-
event_loop,
912-
per_process::v8_platform.Platform(),
913-
allocator.get()),
914-
&FreeIsolateData);
915-
exit_code =
916-
StartNodeWithIsolate(isolate, isolate_data.get(), args, exec_args);
917-
}
918-
919-
isolate->Dispose();
920-
per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
921-
922-
return exit_code;
923-
}
924-
925755
int Start(int argc, char** argv) {
926756
atexit([] () { uv_tty_reset_mode(); });
927757
PlatformInit();
@@ -981,8 +811,13 @@ int Start(int argc, char** argv) {
981811
V8::Initialize();
982812
performance::performance_v8_start = PERFORMANCE_NOW();
983813
per_process::v8_initialized = true;
984-
const int exit_code =
985-
StartNodeWithLoopAndArgs(uv_default_loop(), args, exec_args);
814+
815+
int exit_code = 0;
816+
{
817+
NodeMainInstance main_instance(uv_default_loop(), args, exec_args);
818+
exit_code = main_instance.Run();
819+
}
820+
986821
per_process::v8_initialized = false;
987822
V8::Dispose();
988823

‎src/node_internals.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,9 @@ bool SafeGetenv(const char* key, std::string* text, Environment* env = nullptr);
297297
} // namespace credentials
298298

299299
void DefineZlibConstants(v8::Local<v8::Object> target);
300-
300+
v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params,
301+
uv_loop_t* event_loop,
302+
MultiIsolatePlatform* platform);
301303
v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env);
302304
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
303305
const char* main_script_id);

‎src/node_main_instance.cc

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#include "node_main_instance.h"
2+
#include "node_internals.h"
3+
#include "node_options-inl.h"
4+
#include "node_v8_platform-inl.h"
5+
6+
namespace node {
7+
8+
using v8::Context;
9+
using v8::HandleScope;
10+
using v8::Isolate;
11+
using v8::Local;
12+
using v8::Locker;
13+
using v8::SealHandleScope;
14+
15+
NodeMainInstance::NodeMainInstance(uv_loop_t* event_loop,
16+
const std::vector<std::string>& args,
17+
const std::vector<std::string>& exec_args)
18+
: args_(args),
19+
exec_args_(exec_args),
20+
array_buffer_allocator_(ArrayBufferAllocator::Create()),
21+
isolate_(nullptr),
22+
isolate_data_(nullptr) {
23+
// TODO(joyeecheung): when we implement snapshot integration this needs to
24+
// set params.external_references.
25+
Isolate::CreateParams params;
26+
params.array_buffer_allocator = array_buffer_allocator_.get();
27+
isolate_ =
28+
NewIsolate(&params, event_loop, per_process::v8_platform.Platform());
29+
CHECK_NOT_NULL(isolate_);
30+
isolate_data_.reset(CreateIsolateData(isolate_,
31+
event_loop,
32+
per_process::v8_platform.Platform(),
33+
array_buffer_allocator_.get()));
34+
}
35+
36+
NodeMainInstance::~NodeMainInstance() {
37+
isolate_->Dispose();
38+
per_process::v8_platform.Platform()->UnregisterIsolate(isolate_);
39+
}
40+
41+
int NodeMainInstance::Run() {
42+
Locker locker(isolate_);
43+
Isolate::Scope isolate_scope(isolate_);
44+
HandleScope handle_scope(isolate_);
45+
46+
int exit_code = 0;
47+
std::unique_ptr<Environment> env = CreateMainEnvironment(&exit_code);
48+
49+
CHECK_NOT_NULL(env);
50+
Context::Scope context_scope(env->context());
51+
52+
if (exit_code == 0) {
53+
{
54+
AsyncCallbackScope callback_scope(env.get());
55+
env->async_hooks()->push_async_ids(1, 0);
56+
LoadEnvironment(env.get());
57+
env->async_hooks()->pop_async_id(1);
58+
}
59+
60+
{
61+
SealHandleScope seal(isolate_);
62+
bool more;
63+
env->performance_state()->Mark(
64+
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
65+
do {
66+
uv_run(env->event_loop(), UV_RUN_DEFAULT);
67+
68+
per_process::v8_platform.DrainVMTasks(isolate_);
69+
70+
more = uv_loop_alive(env->event_loop());
71+
if (more && !env->is_stopping()) continue;
72+
73+
env->RunBeforeExitCallbacks();
74+
75+
if (!uv_loop_alive(env->event_loop())) {
76+
EmitBeforeExit(env.get());
77+
}
78+
79+
// Emit `beforeExit` if the loop became alive either after emitting
80+
// event, or after running some callbacks.
81+
more = uv_loop_alive(env->event_loop());
82+
} while (more == true && !env->is_stopping());
83+
env->performance_state()->Mark(
84+
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
85+
}
86+
87+
env->set_trace_sync_io(false);
88+
exit_code = EmitExit(env.get());
89+
WaitForInspectorDisconnect(env.get());
90+
}
91+
92+
env->set_can_call_into_js(false);
93+
env->stop_sub_worker_contexts();
94+
uv_tty_reset_mode();
95+
env->RunCleanup();
96+
RunAtExit(env.get());
97+
98+
per_process::v8_platform.DrainVMTasks(isolate_);
99+
per_process::v8_platform.CancelVMTasks(isolate_);
100+
101+
#if defined(LEAK_SANITIZER)
102+
__lsan_do_leak_check();
103+
#endif
104+
105+
return exit_code;
106+
}
107+
108+
// TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h
109+
// and the environment creation routine in workers somehow.
110+
std::unique_ptr<Environment> NodeMainInstance::CreateMainEnvironment(
111+
int* exit_code) {
112+
*exit_code = 0; // Reset the exit code to 0
113+
114+
HandleScope handle_scope(isolate_);
115+
116+
// TODO(addaleax): This should load a real per-Isolate option, currently
117+
// this is still effectively per-process.
118+
if (isolate_data_->options()->track_heap_objects) {
119+
isolate_->GetHeapProfiler()->StartTrackingHeapObjects(true);
120+
}
121+
122+
Local<Context> context = NewContext(isolate_);
123+
Context::Scope context_scope(context);
124+
125+
std::unique_ptr<Environment> env = std::make_unique<Environment>(
126+
isolate_data_.get(),
127+
context,
128+
static_cast<Environment::Flags>(Environment::kIsMainThread |
129+
Environment::kOwnsProcessState |
130+
Environment::kOwnsInspector));
131+
env->InitializeLibuv(per_process::v8_is_profiling);
132+
env->ProcessCliArgs(args_, exec_args_);
133+
134+
#if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM
135+
CHECK(!env->inspector_agent()->IsListening());
136+
// Inspector agent can't fail to start, but if it was configured to listen
137+
// right away on the websocket port and fails to bind/etc, this will return
138+
// false.
139+
env->inspector_agent()->Start(args_.size() > 1 ? args_[1].c_str() : "",
140+
env->options()->debug_options(),
141+
env->inspector_host_port(),
142+
true);
143+
if (env->options()->debug_options().inspector_enabled &&
144+
!env->inspector_agent()->IsListening()) {
145+
*exit_code = 12; // Signal internal error.
146+
return env;
147+
}
148+
#else
149+
// inspector_enabled can't be true if !HAVE_INSPECTOR or
150+
// !NODE_USE_V8_PLATFORM
151+
// - the option parser should not allow that.
152+
CHECK(!env->options()->debug_options().inspector_enabled);
153+
#endif // HAVE_INSPECTOR && NODE_USE_V8_PLATFORM
154+
155+
if (RunBootstrapping(env.get()).IsEmpty()) {
156+
*exit_code = 1;
157+
}
158+
159+
return env;
160+
}
161+
162+
} // namespace node

‎src/node_main_instance.h

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef SRC_NODE_MAIN_INSTANCE_H_
2+
#define SRC_NODE_MAIN_INSTANCE_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "node.h"
7+
#include "util.h"
8+
#include "uv.h"
9+
#include "v8.h"
10+
11+
namespace node {
12+
13+
// TODO(joyeecheung): align this with the Worker/WorkerThreadData class.
14+
// We may be able to create an abstract class to reuse some of the routines.
15+
class NodeMainInstance {
16+
public:
17+
NodeMainInstance(const NodeMainInstance&) = delete;
18+
NodeMainInstance& operator=(const NodeMainInstance&) = delete;
19+
NodeMainInstance(NodeMainInstance&&) = delete;
20+
NodeMainInstance& operator=(NodeMainInstance&&) = delete;
21+
22+
NodeMainInstance(uv_loop_t* event_loop,
23+
const std::vector<std::string>& args,
24+
const std::vector<std::string>& exec_args);
25+
~NodeMainInstance();
26+
27+
// Start running the Node.js instances, return the exit code when finished.
28+
int Run();
29+
30+
private:
31+
// TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h
32+
// and the environment creation routine in workers somehow.
33+
std::unique_ptr<Environment> CreateMainEnvironment(int* exit_code);
34+
35+
std::vector<std::string> args_;
36+
std::vector<std::string> exec_args_;
37+
std::unique_ptr<ArrayBufferAllocator> array_buffer_allocator_;
38+
v8::Isolate* isolate_;
39+
std::unique_ptr<IsolateData> isolate_data_;
40+
};
41+
42+
} // namespace node
43+
44+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
45+
#endif // SRC_NODE_MAIN_INSTANCE_H_

0 commit comments

Comments
 (0)
Please sign in to comment.