xref: /third_party/node/src/api/embed_helpers.cc (revision 1cb0ef41)
1#include "node.h"
2#include "env-inl.h"
3#include "debug_utils-inl.h"
4
5using v8::Context;
6using v8::Function;
7using v8::Global;
8using v8::HandleScope;
9using v8::Isolate;
10using v8::Local;
11using v8::Locker;
12using v8::Maybe;
13using v8::Nothing;
14using v8::SealHandleScope;
15using v8::TryCatch;
16
17namespace node {
18
19Maybe<int> SpinEventLoop(Environment* env) {
20  CHECK_NOT_NULL(env);
21  MultiIsolatePlatform* platform = GetMultiIsolatePlatform(env);
22  CHECK_NOT_NULL(platform);
23
24  Isolate* isolate = env->isolate();
25  HandleScope handle_scope(isolate);
26  Context::Scope context_scope(env->context());
27  SealHandleScope seal(isolate);
28
29  if (env->is_stopping()) return Nothing<int>();
30
31  env->set_trace_sync_io(env->options()->trace_sync_io);
32  {
33    bool more;
34    env->performance_state()->Mark(
35        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
36    do {
37      if (env->is_stopping()) break;
38      uv_run(env->event_loop(), UV_RUN_DEFAULT);
39      if (env->is_stopping()) break;
40
41      platform->DrainTasks(isolate);
42
43      more = uv_loop_alive(env->event_loop());
44      if (more && !env->is_stopping()) continue;
45
46      if (EmitProcessBeforeExit(env).IsNothing())
47        break;
48
49      {
50        HandleScope handle_scope(isolate);
51        if (env->RunSnapshotSerializeCallback().IsEmpty()) {
52          break;
53        }
54      }
55
56      // Emit `beforeExit` if the loop became alive either after emitting
57      // event, or after running some callbacks.
58      more = uv_loop_alive(env->event_loop());
59    } while (more == true && !env->is_stopping());
60    env->performance_state()->Mark(
61        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
62  }
63  if (env->is_stopping()) return Nothing<int>();
64
65  env->set_trace_sync_io(false);
66  // Clear the serialize callback even though the JS-land queue should
67  // be empty this point so that the deserialized instance won't
68  // attempt to call into JS again.
69  env->set_snapshot_serialize_callback(Local<Function>());
70
71  env->PrintInfoForSnapshotIfDebug();
72  env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); });
73  return EmitProcessExit(env);
74}
75
76struct CommonEnvironmentSetup::Impl {
77  MultiIsolatePlatform* platform = nullptr;
78  uv_loop_t loop;
79  std::shared_ptr<ArrayBufferAllocator> allocator;
80  Isolate* isolate = nullptr;
81  DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data;
82  DeleteFnPtr<Environment, FreeEnvironment> env;
83  Global<Context> context;
84};
85
86CommonEnvironmentSetup::CommonEnvironmentSetup(
87    MultiIsolatePlatform* platform,
88    std::vector<std::string>* errors,
89    std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
90  : impl_(new Impl()) {
91  CHECK_NOT_NULL(platform);
92  CHECK_NOT_NULL(errors);
93
94  impl_->platform = platform;
95  uv_loop_t* loop = &impl_->loop;
96  // Use `data` to tell the destructor whether the loop was initialized or not.
97  loop->data = nullptr;
98  int ret = uv_loop_init(loop);
99  if (ret != 0) {
100    errors->push_back(
101        SPrintF("Failed to initialize loop: %s", uv_err_name(ret)));
102    return;
103  }
104  loop->data = this;
105
106  impl_->allocator = ArrayBufferAllocator::Create();
107  impl_->isolate = NewIsolate(impl_->allocator, &impl_->loop, platform);
108  Isolate* isolate = impl_->isolate;
109
110  {
111    Locker locker(isolate);
112    Isolate::Scope isolate_scope(isolate);
113    HandleScope handle_scope(isolate);
114
115    TryCatch bootstrapCatch(isolate);
116    auto print_Exception = OnScopeLeave([&]() {
117      if (bootstrapCatch.HasCaught()) {
118        errors->push_back(FormatCaughtException(
119            isolate, isolate->GetCurrentContext(), bootstrapCatch));
120      }
121    });
122
123    impl_->isolate_data.reset(CreateIsolateData(
124        isolate, loop, platform, impl_->allocator.get()));
125
126    Local<Context> context = NewContext(isolate);
127    impl_->context.Reset(isolate, context);
128    if (context.IsEmpty()) {
129      errors->push_back("Failed to initialize V8 Context");
130      return;
131    }
132
133    Context::Scope context_scope(context);
134    impl_->env.reset(make_env(this));
135  }
136}
137
138CommonEnvironmentSetup::~CommonEnvironmentSetup() {
139  if (impl_->isolate != nullptr) {
140    Isolate* isolate = impl_->isolate;
141    {
142      Locker locker(isolate);
143      Isolate::Scope isolate_scope(isolate);
144
145      impl_->context.Reset();
146      impl_->env.reset();
147      impl_->isolate_data.reset();
148    }
149
150    bool platform_finished = false;
151    impl_->platform->AddIsolateFinishedCallback(isolate, [](void* data) {
152      *static_cast<bool*>(data) = true;
153    }, &platform_finished);
154    impl_->platform->UnregisterIsolate(isolate);
155    isolate->Dispose();
156
157    // Wait until the platform has cleaned up all relevant resources.
158    while (!platform_finished)
159      uv_run(&impl_->loop, UV_RUN_ONCE);
160  }
161
162  if (impl_->isolate || impl_->loop.data != nullptr)
163    CheckedUvLoopClose(&impl_->loop);
164
165  delete impl_;
166}
167
168
169uv_loop_t* CommonEnvironmentSetup::event_loop() const {
170  return &impl_->loop;
171}
172
173std::shared_ptr<ArrayBufferAllocator>
174CommonEnvironmentSetup::array_buffer_allocator() const {
175  return impl_->allocator;
176}
177
178Isolate* CommonEnvironmentSetup::isolate() const {
179  return impl_->isolate;
180}
181
182IsolateData* CommonEnvironmentSetup::isolate_data() const {
183  return impl_->isolate_data.get();
184}
185
186Environment* CommonEnvironmentSetup::env() const {
187  return impl_->env.get();
188}
189
190v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
191  return impl_->context.Get(impl_->isolate);
192}
193
194}  // namespace node
195