From 808dedc4b3230bcebb5c6272040d7bb6486d09c8 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 11 Nov 2019 14:24:13 +0000 Subject: [PATCH] src: align worker and main thread code with embedder API This addresses some long-standing TODOs by Joyee and me about making the embedder API more powerful and us less reliant on internal APIs for creating the main thread and Workers. Backport-PR-URL: https://github.com/nodejs/node/pull/35241 PR-URL: https://github.com/nodejs/node/pull/30467 Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil --- src/api/environment.cc | 90 ++++++++++++++++++++++++++++++++++++--- src/env-inl.h | 11 +++-- src/env.cc | 47 ++++++++++++++------ src/env.h | 20 ++++----- src/node.cc | 41 +++++++++++++----- src/node.h | 52 +++++++++++++++++++++- src/node_internals.h | 1 + src/node_main_instance.cc | 16 ++----- src/node_main_instance.h | 2 - src/node_worker.cc | 81 +++++++++++++++-------------------- src/node_worker.h | 7 +-- 11 files changed, 253 insertions(+), 115 deletions(-) diff --git a/src/api/environment.cc b/src/api/environment.cc index ee7732c93b8563..2f17e1b2c75100 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -7,6 +7,10 @@ #include "node_v8_platform-inl.h" #include "uv.h" +#if HAVE_INSPECTOR +#include "inspector/worker_inspector.h" // ParentInspectorHandle +#endif + namespace node { using errors::TryCatchScope; using v8::Array; @@ -319,26 +323,40 @@ Environment* CreateEnvironment(IsolateData* isolate_data, const char* const* argv, int exec_argc, const char* const* exec_argv) { + return CreateEnvironment( + isolate_data, context, + std::vector(argv, argv + argc), + std::vector(exec_argv, exec_argv + exec_argc)); +} + +Environment* CreateEnvironment( + IsolateData* isolate_data, + Local context, + const std::vector& args, + const std::vector& exec_args, + EnvironmentFlags::Flags flags, + ThreadId thread_id) { Isolate* isolate = context->GetIsolate(); HandleScope handle_scope(isolate); Context::Scope context_scope(context); // TODO(addaleax): This is a much better place for parsing per-Environment // options than the global parse call. - std::vector args(argv, argv + argc); - std::vector exec_args(exec_argv, exec_argv + exec_argc); - // TODO(addaleax): Provide more sensible flags, in an embedder-accessible way. Environment* env = new Environment( isolate_data, context, args, exec_args, - static_cast(Environment::kOwnsProcessState | - Environment::kOwnsInspector)); - env->InitializeLibuv(per_process::v8_is_profiling); + flags, + thread_id); + if (flags & EnvironmentFlags::kOwnsProcessState) { + env->set_abort_on_uncaught_exception(false); + } + if (env->RunBootstrapping().IsEmpty()) { FreeEnvironment(env); return nullptr; } + return env; } @@ -363,6 +381,58 @@ void FreeEnvironment(Environment* env) { delete env; } +InspectorParentHandle::~InspectorParentHandle() {} + +// Hide the internal handle class from the public API. +#if HAVE_INSPECTOR +struct InspectorParentHandleImpl : public InspectorParentHandle { + std::unique_ptr impl; + + explicit InspectorParentHandleImpl( + std::unique_ptr&& impl) + : impl(std::move(impl)) {} +}; +#endif + +NODE_EXTERN std::unique_ptr GetInspectorParentHandle( + Environment* env, + ThreadId thread_id, + const char* url) { + CHECK_NOT_NULL(env); + CHECK_NE(thread_id.id, static_cast(-1)); +#if HAVE_INSPECTOR + return std::make_unique( + env->inspector_agent()->GetParentHandle(thread_id.id, url)); +#else + return {}; +#endif +} + +void LoadEnvironment(Environment* env) { + USE(LoadEnvironment(env, {})); +} + +MaybeLocal LoadEnvironment( + Environment* env, + std::unique_ptr inspector_parent_handle) { + env->InitializeLibuv(per_process::v8_is_profiling); + env->InitializeDiagnostics(); + +#if HAVE_INSPECTOR + if (inspector_parent_handle) { + env->InitializeInspector( + std::move(static_cast( + inspector_parent_handle.get())->impl)); + } else { + env->InitializeInspector({}); + } +#endif + + // TODO(joyeecheung): Allow embedders to customize the entry + // point more directly without using _third_party_main.js + return StartExecution(env); +} + Environment* GetCurrentEnvironment(Local context) { return Environment::GetCurrent(context); } @@ -579,4 +649,12 @@ void AddLinkedBinding(Environment* env, AddLinkedBinding(env, mod); } +static std::atomic next_thread_id{0}; + +ThreadId AllocateEnvironmentThreadId() { + ThreadId ret; + ret.id = next_thread_id++; + return ret; +} + } // namespace node diff --git a/src/env-inl.h b/src/env-inl.h index b82ac88fc7fafc..598b84c07ca8d2 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -813,8 +813,9 @@ void Environment::SetImmediateThreadsafe(Fn&& cb, CallbackFlags::Flags flags) { { Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_); native_immediates_threadsafe_.Push(std::move(callback)); + if (task_queues_async_initialized_) + uv_async_send(&task_queues_async_); } - uv_async_send(&task_queues_async_); } template @@ -824,8 +825,9 @@ void Environment::RequestInterrupt(Fn&& cb) { { Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_); native_immediates_interrupts_.Push(std::move(callback)); + if (task_queues_async_initialized_) + uv_async_send(&task_queues_async_); } - uv_async_send(&task_queues_async_); RequestInterruptFromV8(); } @@ -858,11 +860,11 @@ inline bool Environment::is_main_thread() const { } inline bool Environment::owns_process_state() const { - return flags_ & kOwnsProcessState; + return flags_ & EnvironmentFlags::kOwnsProcessState; } inline bool Environment::owns_inspector() const { - return flags_ & kOwnsInspector; + return flags_ & EnvironmentFlags::kOwnsInspector; } inline uint64_t Environment::thread_id() const { @@ -1176,6 +1178,7 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) { inline void Environment::RegisterFinalizationGroupForCleanup( v8::Local group) { cleanup_finalization_groups_.emplace_back(isolate(), group); + DCHECK(task_queues_async_initialized_); uv_async_send(&task_queues_async_); } diff --git a/src/env.cc b/src/env.cc index 653f2e3f9e8753..f0b8b521f70363 100644 --- a/src/env.cc +++ b/src/env.cc @@ -257,12 +257,6 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() { USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args)); } -static std::atomic next_thread_id{0}; - -uint64_t Environment::AllocateThreadId() { - return next_thread_id++; -} - void Environment::CreateProperties() { HandleScope handle_scope(isolate_); Local ctx = context(); @@ -319,8 +313,8 @@ Environment::Environment(IsolateData* isolate_data, Local context, const std::vector& args, const std::vector& exec_args, - Flags flags, - uint64_t thread_id) + EnvironmentFlags::Flags flags, + ThreadId thread_id) : isolate_(context->GetIsolate()), isolate_data_(isolate_data), immediate_info_(context->GetIsolate()), @@ -332,7 +326,8 @@ Environment::Environment(IsolateData* isolate_data, should_abort_on_uncaught_toggle_(isolate_, 1), stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields), flags_(flags), - thread_id_(thread_id == kNoThreadId ? AllocateThreadId() : thread_id), + thread_id_(thread_id.id == static_cast(-1) ? + AllocateEnvironmentThreadId().id : thread_id.id), fs_stats_field_array_(isolate_, kFsStatsBufferLength), fs_stats_field_bigint_array_(isolate_, kFsStatsBufferLength), context_(context->GetIsolate(), context) { @@ -340,6 +335,14 @@ Environment::Environment(IsolateData* isolate_data, HandleScope handle_scope(isolate()); Context::Scope context_scope(context); + // Set some flags if only kDefaultFlags was passed. This can make API version + // transitions easier for embedders. + if (flags_ & EnvironmentFlags::kDefaultFlags) { + flags_ = flags_ | + EnvironmentFlags::kOwnsProcessState | + EnvironmentFlags::kOwnsInspector; + } + set_env_vars(per_process::system_environment); enabled_debug_list_.Parse(this); @@ -358,6 +361,10 @@ Environment::Environment(IsolateData* isolate_data, AssignToContext(context, ContextInfo("")); + static uv_once_t init_once = UV_ONCE_INIT; + uv_once(&init_once, InitThreadLocalOnce); + uv_key_set(&thread_local_env, this); + if (tracing::AgentWriterHandle* writer = GetTracingAgentWriter()) { trace_state_observer_ = std::make_unique(this); if (TracingController* tracing_controller = writer->GetTracingController()) @@ -407,6 +414,9 @@ Environment::Environment(IsolateData* isolate_data, Environment::~Environment() { if (interrupt_data_ != nullptr) *interrupt_data_ = nullptr; + // FreeEnvironment() should have set this. + CHECK(is_stopping()); + isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback( BuildEmbedderGraph, this); @@ -493,6 +503,15 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) { uv_unref(reinterpret_cast(&idle_check_handle_)); uv_unref(reinterpret_cast(&task_queues_async_)); + { + Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_); + task_queues_async_initialized_ = true; + if (native_immediates_threadsafe_.size() > 0 || + native_immediates_interrupts_.size() > 0) { + uv_async_send(&task_queues_async_); + } + } + // Register clean-up cb to be called to clean up the handles // when the environment is freed, note that they are not cleaned in // the one environment per process setup, but will be called in @@ -502,10 +521,6 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) { if (start_profiler_idle_notifier) { StartProfilerIdleNotifier(); } - - static uv_once_t init_once = UV_ONCE_INIT; - uv_once(&init_once, InitThreadLocalOnce); - uv_key_set(&thread_local_env, this); } void Environment::ExitEnv() { @@ -539,6 +554,11 @@ void Environment::RegisterHandleCleanups() { } void Environment::CleanupHandles() { + { + Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_); + task_queues_async_initialized_ = false; + } + Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(), Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); @@ -1103,6 +1123,7 @@ void Environment::CleanupFinalizationGroups() { if (try_catch.HasCaught() && !try_catch.HasTerminated()) errors::TriggerUncaughtException(isolate(), try_catch); // Re-schedule the execution of the remainder of the queue. + CHECK(task_queues_async_initialized_); uv_async_send(&task_queues_async_); return; } diff --git a/src/env.h b/src/env.h index 5e9c7c0495366f..8331a79dbc9918 100644 --- a/src/env.h +++ b/src/env.h @@ -874,12 +874,6 @@ class Environment : public MemoryRetainer { inline void PushAsyncCallbackScope(); inline void PopAsyncCallbackScope(); - enum Flags { - kNoFlags = 0, - kOwnsProcessState = 1 << 1, - kOwnsInspector = 1 << 2, - }; - static inline Environment* GetCurrent(v8::Isolate* isolate); static inline Environment* GetCurrent(v8::Local context); static inline Environment* GetCurrent( @@ -898,8 +892,8 @@ class Environment : public MemoryRetainer { v8::Local context, const std::vector& args, const std::vector& exec_args, - Flags flags = Flags(), - uint64_t thread_id = kNoThreadId); + EnvironmentFlags::Flags flags, + ThreadId thread_id); ~Environment() override; void InitializeLibuv(bool start_profiler_idle_notifier); @@ -1068,9 +1062,6 @@ class Environment : public MemoryRetainer { inline bool has_serialized_options() const; inline void set_has_serialized_options(bool has_serialized_options); - static uint64_t AllocateThreadId(); - static constexpr uint64_t kNoThreadId = -1; - inline bool is_main_thread() const; inline bool owns_process_state() const; inline bool owns_inspector() const; @@ -1350,7 +1341,7 @@ class Environment : public MemoryRetainer { bool has_serialized_options_ = false; std::atomic_bool can_call_into_js_ { true }; - Flags flags_; + uint64_t flags_; uint64_t thread_id_; std::unordered_set sub_worker_contexts_; @@ -1409,6 +1400,11 @@ class Environment : public MemoryRetainer { Mutex native_immediates_threadsafe_mutex_; NativeImmediateQueue native_immediates_threadsafe_; NativeImmediateQueue native_immediates_interrupts_; + // Also guarded by native_immediates_threadsafe_mutex_. This can be used when + // trying to post tasks from other threads to an Environment, as the libuv + // handle for the immediate queues (task_queues_async_) may not be initialized + // yet or already have been destroyed. + bool task_queues_async_initialized_ = false; void RunAndClearNativeImmediates(bool only_refed = false); void RunAndClearInterrupts(); diff --git a/src/node.cc b/src/node.cc index e93ddecb872f56..c34a875768315c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -190,8 +190,8 @@ MaybeLocal ExecuteBootstrapper(Environment* env, int Environment::InitializeInspector( std::unique_ptr parent_handle) { std::string inspector_path; + bool is_main = !parent_handle; if (parent_handle) { - DCHECK(!is_main_thread()); inspector_path = parent_handle->url(); inspector_agent_->SetParentHandle(std::move(parent_handle)); } else { @@ -205,7 +205,7 @@ int Environment::InitializeInspector( inspector_agent_->Start(inspector_path, options_->debug_options(), inspector_host_port(), - is_main_thread()); + is_main); if (options_->debug_options().inspector_enabled && !inspector_agent_->IsListening()) { return 12; // Signal internal error @@ -394,7 +394,7 @@ MaybeLocal StartExecution(Environment* env, const char* main_script_id) { ExecuteBootstrapper(env, main_script_id, ¶meters, &arguments)); } -MaybeLocal StartMainThreadExecution(Environment* env) { +MaybeLocal StartExecution(Environment* env) { // To allow people to extend Node in different ways, this hook allows // one to drop a file lib/_third_party_main.js into the build // directory which will be executed instead of Node's normal loading. @@ -402,6 +402,10 @@ MaybeLocal StartMainThreadExecution(Environment* env) { return StartExecution(env, "internal/main/run_third_party_main"); } + if (env->worker_context() != nullptr) { + return StartExecution(env, "internal/main/worker_thread"); + } + std::string first_argv; if (env->argv().size() > 1) { first_argv = env->argv()[1]; @@ -440,15 +444,30 @@ MaybeLocal StartMainThreadExecution(Environment* env) { return StartExecution(env, "internal/main/eval_stdin"); } -void LoadEnvironment(Environment* env) { - CHECK(env->is_main_thread()); - // TODO(joyeecheung): Not all of the execution modes in - // StartMainThreadExecution() make sense for embedders. Pick the - // useful ones out, and allow embedders to customize the entry - // point more directly without using _third_party_main.js - USE(StartMainThreadExecution(env)); +#ifdef __POSIX__ +typedef void (*sigaction_cb)(int signo, siginfo_t* info, void* ucontext); +#endif +#if NODE_USE_V8_WASM_TRAP_HANDLER +static std::atomic previous_sigsegv_action; + +void TrapWebAssemblyOrContinue(int signo, siginfo_t* info, void* ucontext) { + if (!v8::TryHandleWebAssemblyTrapPosix(signo, info, ucontext)) { + sigaction_cb prev = previous_sigsegv_action.load(); + if (prev != nullptr) { + prev(signo, info, ucontext); + } else { + // Reset to the default signal handler, i.e. cause a hard crash. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + CHECK_EQ(sigaction(signo, &sa, nullptr), 0); + + ResetStdio(); + raise(signo); + } + } } - +#endif // NODE_USE_V8_WASM_TRAP_HANDLER #ifdef __POSIX__ void RegisterSignalHandler(int signal, diff --git a/src/node.h b/src/node.h index e9cf73ede5373a..4357291de79cec 100644 --- a/src/node.h +++ b/src/node.h @@ -363,18 +363,66 @@ NODE_EXTERN IsolateData* CreateIsolateData( ArrayBufferAllocator* allocator = nullptr); NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data); -// TODO(addaleax): Add an official variant using STL containers, and move -// per-Environment options parsing here. +struct ThreadId { + uint64_t id = static_cast(-1); +}; +NODE_EXTERN ThreadId AllocateEnvironmentThreadId(); + +namespace EnvironmentFlags { +enum Flags : uint64_t { + kNoFlags = 0, + // Use the default behaviour for Node.js instances. + kDefaultFlags = 1 << 0, + // Controls whether this Environment is allowed to affect per-process state + // (e.g. cwd, process title, uid, etc.). + // This is set when using kDefaultFlags. + kOwnsProcessState = 1 << 1, + // Set if this Environment instance is associated with the global inspector + // handling code (i.e. listening on SIGUSR1). + // This is set when using kDefaultFlags. + kOwnsInspector = 1 << 2 +}; +} // namespace EnvironmentFlags + +// TODO(addaleax): Maybe move per-Environment options parsing here. // Returns nullptr when the Environment cannot be created e.g. there are // pending JavaScript exceptions. +// It is recommended to use the second variant taking a flags argument. NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data, v8::Local context, int argc, const char* const* argv, int exec_argc, const char* const* exec_argv); +NODE_EXTERN Environment* CreateEnvironment( + IsolateData* isolate_data, + v8::Local context, + const std::vector& args, + const std::vector& exec_args, + EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags, + ThreadId thread_id = {} /* allocates a thread id automatically */); +struct InspectorParentHandle { + virtual ~InspectorParentHandle(); +}; +// Returns a handle that can be passed to `LoadEnvironment()`, making the +// child Environment accessible to the inspector as if it were a Node.js Worker. +// `child_thread_id` can be created using `AllocateEnvironmentThreadId()` +// and then later passed on to `CreateEnvironment()` to create the child +// Environment. +// This method should not be called while the parent Environment is active +// on another thread. +NODE_EXTERN std::unique_ptr GetInspectorParentHandle( + Environment* parent_env, + ThreadId child_thread_id, + const char* child_url); + +// TODO(addaleax): Deprecate this in favour of the MaybeLocal<> overload +// and provide a more flexible approach than third_party_main. NODE_EXTERN void LoadEnvironment(Environment* env); +NODE_EXTERN v8::MaybeLocal LoadEnvironment( + Environment* env, + std::unique_ptr inspector_parent_handle); NODE_EXTERN void FreeEnvironment(Environment* env); // This may return nullptr if context is not associated with a Node instance. diff --git a/src/node_internals.h b/src/node_internals.h index c1555b312e2f22..38de262e05e8d9 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -301,6 +301,7 @@ void DefineZlibConstants(v8::Local target); v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform); +v8::MaybeLocal StartExecution(Environment* env); v8::MaybeLocal StartExecution(Environment* env, const char* main_script_id); v8::MaybeLocal GetPerContextExports(v8::Local context); diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index cbbff1ad742006..f638e26dba5a04 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -172,8 +172,6 @@ int NodeMainInstance::Run() { return exit_code; } -// TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h -// and the environment creation routine in workers somehow. DeleteFnPtr NodeMainInstance::CreateMainEnvironment(int* exit_code) { *exit_code = 0; // Reset the exit code to 0 @@ -199,26 +197,18 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) { CHECK(!context.IsEmpty()); Context::Scope context_scope(context); - DeleteFnPtr env { new Environment( + DeleteFnPtr env { CreateEnvironment( isolate_data_.get(), context, args_, exec_args_, - static_cast(Environment::kOwnsProcessState | - Environment::kOwnsInspector)) }; - env->InitializeLibuv(per_process::v8_is_profiling); - env->InitializeDiagnostics(); + EnvironmentFlags::kDefaultFlags) }; - // TODO(joyeecheung): when we snapshot the bootstrapped context, - // the inspector and diagnostics setup should after after deserialization. -#if HAVE_INSPECTOR - *exit_code = env->InitializeInspector({}); -#endif if (*exit_code != 0) { return env; } - if (env->RunBootstrapping().IsEmpty()) { + if (env == nullptr) { *exit_code = 1; } diff --git a/src/node_main_instance.h b/src/node_main_instance.h index cc9f50b9222de3..b8178c2774e795 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -59,8 +59,6 @@ class NodeMainInstance { IsolateData* isolate_data() { return isolate_data_.get(); } - // TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h - // and the environment creation routine in workers somehow. DeleteFnPtr CreateMainEnvironment( int* exit_code); diff --git a/src/node_worker.cc b/src/node_worker.cc index 5cac7d0a975de5..dcce464cf80f88 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -8,10 +8,6 @@ #include "util-inl.h" #include "async_wrap-inl.h" -#if HAVE_INSPECTOR -#include "inspector/worker_inspector.h" // ParentInspectorHandle -#endif - #include #include #include @@ -54,10 +50,10 @@ Worker::Worker(Environment* env, exec_argv_(exec_argv), platform_(env->isolate_data()->platform()), array_buffer_allocator_(ArrayBufferAllocator::Create()), - start_profiler_idle_notifier_(env->profiler_idle_notifier_started()), - thread_id_(Environment::AllocateThreadId()), + thread_id_(AllocateEnvironmentThreadId()), env_vars_(env_vars) { - Debug(this, "Creating new worker instance with thread id %llu", thread_id_); + Debug(this, "Creating new worker instance with thread id %llu", + thread_id_.id); // Set up everything that needs to be set up in the parent environment. parent_port_ = MessagePort::New(env, env->context()); @@ -75,19 +71,17 @@ Worker::Worker(Environment* env, object()->Set(env->context(), env->thread_id_string(), - Number::New(env->isolate(), static_cast(thread_id_))) + Number::New(env->isolate(), static_cast(thread_id_.id))) .Check(); -#if HAVE_INSPECTOR - inspector_parent_handle_ = - env->inspector_agent()->GetParentHandle(thread_id_, url); -#endif + inspector_parent_handle_ = GetInspectorParentHandle( + env, thread_id_, url.c_str()); argv_ = std::vector{env->argv()[0]}; // Mark this Worker object as weak until we actually start the thread. MakeWeak(); - Debug(this, "Preparation for worker %llu finished", thread_id_); + Debug(this, "Preparation for worker %llu finished", thread_id_.id); } bool Worker::is_stopped() const { @@ -193,7 +187,7 @@ class WorkerThreadData { } ~WorkerThreadData() { - Debug(w_, "Worker %llu dispose isolate", w_->thread_id_); + Debug(w_, "Worker %llu dispose isolate", w_->thread_id_.id); Isolate* isolate; { Mutex::ScopedLock lock(w_->mutex_); @@ -254,19 +248,19 @@ size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit, void Worker::Run() { std::string name = "WorkerThread "; - name += std::to_string(thread_id_); + name += std::to_string(thread_id_.id); TRACE_EVENT_METADATA1( "__metadata", "thread_name", "name", TRACE_STR_COPY(name.c_str())); CHECK_NOT_NULL(platform_); - Debug(this, "Creating isolate for worker with id %llu", thread_id_); + Debug(this, "Creating isolate for worker with id %llu", thread_id_.id); WorkerThreadData data(this); if (isolate_ == nullptr) return; CHECK(data.loop_is_usable()); - Debug(this, "Starting worker with id %llu", thread_id_); + Debug(this, "Starting worker with id %llu", thread_id_.id); { Locker locker(isolate_); Isolate::Scope isolate_scope(isolate_); @@ -317,42 +311,34 @@ void Worker::Run() { CHECK(!context.IsEmpty()); Context::Scope context_scope(context); { - // TODO(addaleax): Use CreateEnvironment(), or generally another - // public API. - env_.reset(new Environment(data.isolate_data_.get(), - context, - std::move(argv_), - std::move(exec_argv_), - Environment::kNoFlags, - thread_id_)); + env_.reset(CreateEnvironment( + data.isolate_data_.get(), + context, + std::move(argv_), + std::move(exec_argv_), + EnvironmentFlags::kNoFlags, + thread_id_)); + if (is_stopped()) return; CHECK_NOT_NULL(env_); env_->set_env_vars(std::move(env_vars_)); - env_->set_abort_on_uncaught_exception(false); - - env_->InitializeLibuv(start_profiler_idle_notifier_); } { Mutex::ScopedLock lock(mutex_); if (stopped_) return; this->env_ = env_.get(); } - Debug(this, "Created Environment for worker with id %llu", thread_id_); + Debug(this, "Created Environment for worker with id %llu", thread_id_.id); if (is_stopped()) return; { - env_->InitializeDiagnostics(); -#if HAVE_INSPECTOR - env_->InitializeInspector(std::move(inspector_parent_handle_)); -#endif - HandleScope handle_scope(isolate_); - - if (!env_->RunBootstrapping().IsEmpty()) { - CreateEnvMessagePort(env_.get()); - if (is_stopped()) return; - Debug(this, "Created message port for worker %llu", thread_id_); - USE(StartExecution(env_.get(), "internal/main/worker_thread")); + CreateEnvMessagePort(env_.get()); + Debug(this, "Created message port for worker %llu", thread_id_.id); + if (LoadEnvironment(env_.get(), + std::move(inspector_parent_handle_)) + .IsEmpty()) { + return; } - Debug(this, "Loaded environment for worker %llu", thread_id_); + Debug(this, "Loaded environment for worker %llu", thread_id_.id); } if (is_stopped()) return; @@ -392,11 +378,11 @@ void Worker::Run() { exit_code_ = exit_code; Debug(this, "Exiting thread for worker %llu with exit code %d", - thread_id_, exit_code_); + thread_id_.id, exit_code_); } } - Debug(this, "Worker %llu thread stops", thread_id_); + Debug(this, "Worker %llu thread stops", thread_id_.id); } void Worker::CreateEnvMessagePort(Environment* env) { @@ -455,7 +441,7 @@ Worker::~Worker() { CHECK_NULL(env_); CHECK(thread_joined_); - Debug(this, "Worker %llu destroyed", thread_id_); + Debug(this, "Worker %llu destroyed", thread_id_.id); } void Worker::New(const FunctionCallbackInfo& args) { @@ -663,7 +649,7 @@ void Worker::StopThread(const FunctionCallbackInfo& args) { Worker* w; ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); - Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_); + Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_.id); w->Exit(1); } @@ -699,7 +685,7 @@ Local Worker::GetResourceLimits(Isolate* isolate) const { void Worker::Exit(int code) { Mutex::ScopedLock lock(mutex_); - Debug(this, "Worker %llu called Exit(%d)", thread_id_, code); + Debug(this, "Worker %llu called Exit(%d)", thread_id_.id, code); if (env_ != nullptr) { exit_code_ = code; Stop(env_); @@ -726,7 +712,7 @@ void Worker::TakeHeapSnapshot(const FunctionCallbackInfo& args) { Worker* w; ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); - Debug(w, "Worker %llu taking heap snapshot", w->thread_id_); + Debug(w, "Worker %llu taking heap snapshot", w->thread_id_.id); Environment* env = w->env(); AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w); @@ -766,6 +752,7 @@ namespace { void GetEnvMessagePort(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local port = env->message_port(); + CHECK_IMPLIES(!env->is_main_thread(), !port.IsEmpty()); if (!port.IsEmpty()) { CHECK_EQ(port->CreationContext()->GetIsolate(), args.GetIsolate()); args.GetReturnValue().Set(port); diff --git a/src/node_worker.h b/src/node_worker.h index 3611a8536fe040..a7d02914826563 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -75,12 +75,9 @@ class Worker : public AsyncWrap { MultiIsolatePlatform* platform_; std::shared_ptr array_buffer_allocator_; v8::Isolate* isolate_ = nullptr; - bool start_profiler_idle_notifier_; uv_thread_t tid_; -#if HAVE_INSPECTOR - std::unique_ptr inspector_parent_handle_; -#endif + std::unique_ptr inspector_parent_handle_; // This mutex protects access to all variables listed below it. mutable Mutex mutex_; @@ -89,7 +86,7 @@ class Worker : public AsyncWrap { const char* custom_error_ = nullptr; std::string custom_error_str_; int exit_code_ = 0; - uint64_t thread_id_ = -1; + ThreadId thread_id_; uintptr_t stack_base_ = 0; // Custom resource constraints: