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