1#include "env-inl.h" 2#include "node_internals.h" 3#include "node_process-inl.h" 4#include "async_wrap.h" 5 6namespace node { 7 8using v8::Context; 9using v8::HandleScope; 10using v8::Integer; 11using v8::Isolate; 12using v8::Just; 13using v8::Local; 14using v8::Maybe; 15using v8::NewStringType; 16using v8::Nothing; 17using v8::Object; 18using v8::String; 19using v8::Value; 20 21void RunAtExit(Environment* env) { 22 env->RunAtExitCallbacks(); 23} 24 25void AtExit(Environment* env, void (*cb)(void* arg), void* arg) { 26 CHECK_NOT_NULL(env); 27 env->AtExit(cb, arg); 28} 29 30void EmitBeforeExit(Environment* env) { 31 USE(EmitProcessBeforeExit(env)); 32} 33 34Maybe<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 56int EmitExit(Environment* env) { 57 return EmitProcessExit(env).FromMaybe(1); 58} 59 60Maybe<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 89typedef void (*CleanupHook)(void* arg); 90typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*); 91 92struct 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. 106struct 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. 114void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; } 115 116void 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 124void 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 132static 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 140static 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 147ACHHandle* 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 162void 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 169void 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 179async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) { 180 Environment* env = Environment::GetCurrent(isolate); 181 if (env == nullptr) return -1; 182 return env->execution_async_id(); 183} 184 185async_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 192async_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 203async_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 227void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) { 228 EmitAsyncDestroy(Environment::GetCurrent(isolate), asyncContext); 229} 230 231void EmitAsyncDestroy(Environment* env, async_context asyncContext) { 232 AsyncWrap::EmitDestroy(env, asyncContext.async_id); 233} 234 235} // namespace node 236