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