• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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