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