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