• 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(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