1 #include "env-inl.h"
2 #include "node_internals.h"
3 #include "node_process-inl.h"
4 #include "async_wrap.h"
5
6 namespace node {
7
8 using v8::Context;
9 using v8::HandleScope;
10 using v8::Integer;
11 using v8::Isolate;
12 using v8::Just;
13 using v8::Local;
14 using v8::Maybe;
15 using v8::NewStringType;
16 using v8::Nothing;
17 using v8::Object;
18 using v8::String;
19 using v8::Value;
20
RunAtExit(Environment * env)21 void RunAtExit(Environment* env) {
22 env->RunAtExitCallbacks();
23 }
24
AtExit(Environment * env,void (* cb)(void * arg),void * arg)25 void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
26 CHECK_NOT_NULL(env);
27 env->AtExit(cb, arg);
28 }
29
EmitBeforeExit(Environment * env)30 void EmitBeforeExit(Environment* env) {
31 USE(EmitProcessBeforeExit(env));
32 }
33
EmitProcessBeforeExit(Environment * env)34 Maybe<bool> EmitProcessBeforeExit(Environment* env) {
35 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "BeforeExit");
36 if (!env->destroy_async_id_list()->empty())
37 AsyncWrap::DestroyAsyncIdsCallback(env);
38
39 HandleScope handle_scope(env->isolate());
40 Local<Context> context = env->context();
41 Context::Scope context_scope(context);
42
43 Local<Value> exit_code_v;
44 if (!env->process_object()->Get(context, env->exit_code_string())
45 .ToLocal(&exit_code_v)) return Nothing<bool>();
46
47 Local<Integer> exit_code;
48 if (!exit_code_v->ToInteger(context).ToLocal(&exit_code)) {
49 return Nothing<bool>();
50 }
51
52 return ProcessEmit(env, "beforeExit", exit_code).IsEmpty() ?
53 Nothing<bool>() : Just(true);
54 }
55
EmitExit(Environment * env)56 int EmitExit(Environment* env) {
57 return EmitProcessExit(env).FromMaybe(1);
58 }
59
EmitProcessExit(Environment * env)60 Maybe<int> EmitProcessExit(Environment* env) {
61 // process.emit('exit')
62 Isolate* isolate = env->isolate();
63 HandleScope handle_scope(isolate);
64 Local<Context> context = env->context();
65 Context::Scope context_scope(context);
66 Local<Object> process_object = env->process_object();
67
68 // TODO(addaleax): It might be nice to share process.exitCode via
69 // getter/setter pairs that pass data directly to the native side, so that we
70 // don't manually have to read and write JS properties here. These getters
71 // could use e.g. a typed array for performance.
72 env->set_exiting(true);
73
74 Local<String> exit_code = env->exit_code_string();
75 Local<Value> code_v;
76 int code;
77 if (!process_object->Get(context, exit_code).ToLocal(&code_v) ||
78 !code_v->Int32Value(context).To(&code) ||
79 ProcessEmit(env, "exit", Integer::New(isolate, code)).IsEmpty() ||
80 // Reload exit code, it may be changed by `emit('exit')`
81 !process_object->Get(context, exit_code).ToLocal(&code_v) ||
82 !code_v->Int32Value(context).To(&code)) {
83 return Nothing<int>();
84 }
85
86 return Just(code);
87 }
88
89 typedef void (*CleanupHook)(void* arg);
90 typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
91
92 struct AsyncCleanupHookInfo final {
93 Environment* env;
94 AsyncCleanupHook fun;
95 void* arg;
96 bool started = false;
97 // Use a self-reference to make sure the storage is kept alive while the
98 // cleanup hook is registered but not yet finished.
99 std::shared_ptr<AsyncCleanupHookInfo> self;
100 };
101
102 // Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
103 // (but not publicly so for easier ABI/API changes). In particular,
104 // std::shared_ptr does not generally maintain a consistent ABI even on a
105 // specific platform.
106 struct ACHHandle final {
107 std::shared_ptr<AsyncCleanupHookInfo> info;
108 };
109 // This is implemented as an operator on a struct because otherwise you can't
110 // default-initialize AsyncCleanupHookHandle, because in C++ for a
111 // std::unique_ptr to be default-initializable the deleter type also needs
112 // to be default-initializable; in particular, function types don't satisfy
113 // this.
operator ()(ACHHandle * handle) const114 void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
115
AddEnvironmentCleanupHook(Isolate * isolate,CleanupHook fun,void * arg)116 void AddEnvironmentCleanupHook(Isolate* isolate,
117 CleanupHook fun,
118 void* arg) {
119 Environment* env = Environment::GetCurrent(isolate);
120 CHECK_NOT_NULL(env);
121 env->AddCleanupHook(fun, arg);
122 }
123
RemoveEnvironmentCleanupHook(Isolate * isolate,CleanupHook fun,void * arg)124 void RemoveEnvironmentCleanupHook(Isolate* isolate,
125 CleanupHook fun,
126 void* arg) {
127 Environment* env = Environment::GetCurrent(isolate);
128 CHECK_NOT_NULL(env);
129 env->RemoveCleanupHook(fun, arg);
130 }
131
FinishAsyncCleanupHook(void * arg)132 static void FinishAsyncCleanupHook(void* arg) {
133 AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
134 std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
135
136 info->env->DecreaseWaitingRequestCounter();
137 info->self.reset();
138 }
139
RunAsyncCleanupHook(void * arg)140 static void RunAsyncCleanupHook(void* arg) {
141 AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
142 info->env->IncreaseWaitingRequestCounter();
143 info->started = true;
144 info->fun(info->arg, FinishAsyncCleanupHook, info);
145 }
146
AddEnvironmentCleanupHookInternal(Isolate * isolate,AsyncCleanupHook fun,void * arg)147 ACHHandle* AddEnvironmentCleanupHookInternal(
148 Isolate* isolate,
149 AsyncCleanupHook fun,
150 void* arg) {
151 Environment* env = Environment::GetCurrent(isolate);
152 CHECK_NOT_NULL(env);
153 auto info = std::make_shared<AsyncCleanupHookInfo>();
154 info->env = env;
155 info->fun = fun;
156 info->arg = arg;
157 info->self = info;
158 env->AddCleanupHook(RunAsyncCleanupHook, info.get());
159 return new ACHHandle { info };
160 }
161
RemoveEnvironmentCleanupHookInternal(ACHHandle * handle)162 void RemoveEnvironmentCleanupHookInternal(
163 ACHHandle* handle) {
164 if (handle->info->started) return;
165 handle->info->self.reset();
166 handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
167 }
168
RequestInterrupt(Environment * env,void (* fun)(void * arg),void * arg)169 void RequestInterrupt(Environment* env, void (*fun)(void* arg), void* arg) {
170 env->RequestInterrupt([fun, arg](Environment* env) {
171 // Disallow JavaScript execution during interrupt.
172 Isolate::DisallowJavascriptExecutionScope scope(
173 env->isolate(),
174 Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
175 fun(arg);
176 });
177 }
178
AsyncHooksGetExecutionAsyncId(Isolate * isolate)179 async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
180 Environment* env = Environment::GetCurrent(isolate);
181 if (env == nullptr) return -1;
182 return env->execution_async_id();
183 }
184
AsyncHooksGetTriggerAsyncId(Isolate * isolate)185 async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) {
186 Environment* env = Environment::GetCurrent(isolate);
187 if (env == nullptr) return -1;
188 return env->trigger_async_id();
189 }
190
191
EmitAsyncInit(Isolate * isolate,Local<Object> resource,const char * name,async_id trigger_async_id)192 async_context EmitAsyncInit(Isolate* isolate,
193 Local<Object> resource,
194 const char* name,
195 async_id trigger_async_id) {
196 HandleScope handle_scope(isolate);
197 Local<String> type =
198 String::NewFromUtf8(isolate, name, NewStringType::kInternalized)
199 .ToLocalChecked();
200 return EmitAsyncInit(isolate, resource, type, trigger_async_id);
201 }
202
EmitAsyncInit(Isolate * isolate,Local<Object> resource,Local<String> name,async_id trigger_async_id)203 async_context EmitAsyncInit(Isolate* isolate,
204 Local<Object> resource,
205 Local<String> name,
206 async_id trigger_async_id) {
207 DebugSealHandleScope handle_scope(isolate);
208 Environment* env = Environment::GetCurrent(isolate);
209 CHECK_NOT_NULL(env);
210
211 // Initialize async context struct
212 if (trigger_async_id == -1)
213 trigger_async_id = env->get_default_trigger_async_id();
214
215 async_context context = {
216 env->new_async_id(), // async_id_
217 trigger_async_id // trigger_async_id_
218 };
219
220 // Run init hooks
221 AsyncWrap::EmitAsyncInit(env, resource, name, context.async_id,
222 context.trigger_async_id);
223
224 return context;
225 }
226
EmitAsyncDestroy(Isolate * isolate,async_context asyncContext)227 void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) {
228 EmitAsyncDestroy(Environment::GetCurrent(isolate), asyncContext);
229 }
230
EmitAsyncDestroy(Environment * env,async_context asyncContext)231 void EmitAsyncDestroy(Environment* env, async_context asyncContext) {
232 AsyncWrap::EmitDestroy(env, asyncContext.async_id);
233 }
234
235 } // namespace node
236