Skip to content

Commit 808dedc

Browse files
committed
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: #35241 PR-URL: #30467 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent e809a5c commit 808dedc

11 files changed

+253
-115
lines changed

src/api/environment.cc

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include "node_v8_platform-inl.h"
88
#include "uv.h"
99

10+
#if HAVE_INSPECTOR
11+
#include "inspector/worker_inspector.h" // ParentInspectorHandle
12+
#endif
13+
1014
namespace node {
1115
using errors::TryCatchScope;
1216
using v8::Array;
@@ -319,26 +323,40 @@ Environment* CreateEnvironment(IsolateData* isolate_data,
319323
const char* const* argv,
320324
int exec_argc,
321325
const char* const* exec_argv) {
326+
return CreateEnvironment(
327+
isolate_data, context,
328+
std::vector<std::string>(argv, argv + argc),
329+
std::vector<std::string>(exec_argv, exec_argv + exec_argc));
330+
}
331+
332+
Environment* CreateEnvironment(
333+
IsolateData* isolate_data,
334+
Local<Context> context,
335+
const std::vector<std::string>& args,
336+
const std::vector<std::string>& exec_args,
337+
EnvironmentFlags::Flags flags,
338+
ThreadId thread_id) {
322339
Isolate* isolate = context->GetIsolate();
323340
HandleScope handle_scope(isolate);
324341
Context::Scope context_scope(context);
325342
// TODO(addaleax): This is a much better place for parsing per-Environment
326343
// options than the global parse call.
327-
std::vector<std::string> args(argv, argv + argc);
328-
std::vector<std::string> exec_args(exec_argv, exec_argv + exec_argc);
329-
// TODO(addaleax): Provide more sensible flags, in an embedder-accessible way.
330344
Environment* env = new Environment(
331345
isolate_data,
332346
context,
333347
args,
334348
exec_args,
335-
static_cast<Environment::Flags>(Environment::kOwnsProcessState |
336-
Environment::kOwnsInspector));
337-
env->InitializeLibuv(per_process::v8_is_profiling);
349+
flags,
350+
thread_id);
351+
if (flags & EnvironmentFlags::kOwnsProcessState) {
352+
env->set_abort_on_uncaught_exception(false);
353+
}
354+
338355
if (env->RunBootstrapping().IsEmpty()) {
339356
FreeEnvironment(env);
340357
return nullptr;
341358
}
359+
342360
return env;
343361
}
344362

@@ -363,6 +381,58 @@ void FreeEnvironment(Environment* env) {
363381
delete env;
364382
}
365383

384+
InspectorParentHandle::~InspectorParentHandle() {}
385+
386+
// Hide the internal handle class from the public API.
387+
#if HAVE_INSPECTOR
388+
struct InspectorParentHandleImpl : public InspectorParentHandle {
389+
std::unique_ptr<inspector::ParentInspectorHandle> impl;
390+
391+
explicit InspectorParentHandleImpl(
392+
std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
393+
: impl(std::move(impl)) {}
394+
};
395+
#endif
396+
397+
NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
398+
Environment* env,
399+
ThreadId thread_id,
400+
const char* url) {
401+
CHECK_NOT_NULL(env);
402+
CHECK_NE(thread_id.id, static_cast<uint64_t>(-1));
403+
#if HAVE_INSPECTOR
404+
return std::make_unique<InspectorParentHandleImpl>(
405+
env->inspector_agent()->GetParentHandle(thread_id.id, url));
406+
#else
407+
return {};
408+
#endif
409+
}
410+
411+
void LoadEnvironment(Environment* env) {
412+
USE(LoadEnvironment(env, {}));
413+
}
414+
415+
MaybeLocal<Value> LoadEnvironment(
416+
Environment* env,
417+
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
418+
env->InitializeLibuv(per_process::v8_is_profiling);
419+
env->InitializeDiagnostics();
420+
421+
#if HAVE_INSPECTOR
422+
if (inspector_parent_handle) {
423+
env->InitializeInspector(
424+
std::move(static_cast<InspectorParentHandleImpl*>(
425+
inspector_parent_handle.get())->impl));
426+
} else {
427+
env->InitializeInspector({});
428+
}
429+
#endif
430+
431+
// TODO(joyeecheung): Allow embedders to customize the entry
432+
// point more directly without using _third_party_main.js
433+
return StartExecution(env);
434+
}
435+
366436
Environment* GetCurrentEnvironment(Local<Context> context) {
367437
return Environment::GetCurrent(context);
368438
}
@@ -579,4 +649,12 @@ void AddLinkedBinding(Environment* env,
579649
AddLinkedBinding(env, mod);
580650
}
581651

652+
static std::atomic<uint64_t> next_thread_id{0};
653+
654+
ThreadId AllocateEnvironmentThreadId() {
655+
ThreadId ret;
656+
ret.id = next_thread_id++;
657+
return ret;
658+
}
659+
582660
} // namespace node

src/env-inl.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -813,8 +813,9 @@ void Environment::SetImmediateThreadsafe(Fn&& cb, CallbackFlags::Flags flags) {
813813
{
814814
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
815815
native_immediates_threadsafe_.Push(std::move(callback));
816+
if (task_queues_async_initialized_)
817+
uv_async_send(&task_queues_async_);
816818
}
817-
uv_async_send(&task_queues_async_);
818819
}
819820

820821
template <typename Fn>
@@ -824,8 +825,9 @@ void Environment::RequestInterrupt(Fn&& cb) {
824825
{
825826
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
826827
native_immediates_interrupts_.Push(std::move(callback));
828+
if (task_queues_async_initialized_)
829+
uv_async_send(&task_queues_async_);
827830
}
828-
uv_async_send(&task_queues_async_);
829831
RequestInterruptFromV8();
830832
}
831833

@@ -858,11 +860,11 @@ inline bool Environment::is_main_thread() const {
858860
}
859861

860862
inline bool Environment::owns_process_state() const {
861-
return flags_ & kOwnsProcessState;
863+
return flags_ & EnvironmentFlags::kOwnsProcessState;
862864
}
863865

864866
inline bool Environment::owns_inspector() const {
865-
return flags_ & kOwnsInspector;
867+
return flags_ & EnvironmentFlags::kOwnsInspector;
866868
}
867869

868870
inline uint64_t Environment::thread_id() const {
@@ -1176,6 +1178,7 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) {
11761178
inline void Environment::RegisterFinalizationGroupForCleanup(
11771179
v8::Local<v8::FinalizationGroup> group) {
11781180
cleanup_finalization_groups_.emplace_back(isolate(), group);
1181+
DCHECK(task_queues_async_initialized_);
11791182
uv_async_send(&task_queues_async_);
11801183
}
11811184

src/env.cc

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,6 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
257257
USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args));
258258
}
259259

260-
static std::atomic<uint64_t> next_thread_id{0};
261-
262-
uint64_t Environment::AllocateThreadId() {
263-
return next_thread_id++;
264-
}
265-
266260
void Environment::CreateProperties() {
267261
HandleScope handle_scope(isolate_);
268262
Local<Context> ctx = context();
@@ -319,8 +313,8 @@ Environment::Environment(IsolateData* isolate_data,
319313
Local<Context> context,
320314
const std::vector<std::string>& args,
321315
const std::vector<std::string>& exec_args,
322-
Flags flags,
323-
uint64_t thread_id)
316+
EnvironmentFlags::Flags flags,
317+
ThreadId thread_id)
324318
: isolate_(context->GetIsolate()),
325319
isolate_data_(isolate_data),
326320
immediate_info_(context->GetIsolate()),
@@ -332,14 +326,23 @@ Environment::Environment(IsolateData* isolate_data,
332326
should_abort_on_uncaught_toggle_(isolate_, 1),
333327
stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields),
334328
flags_(flags),
335-
thread_id_(thread_id == kNoThreadId ? AllocateThreadId() : thread_id),
329+
thread_id_(thread_id.id == static_cast<uint64_t>(-1) ?
330+
AllocateEnvironmentThreadId().id : thread_id.id),
336331
fs_stats_field_array_(isolate_, kFsStatsBufferLength),
337332
fs_stats_field_bigint_array_(isolate_, kFsStatsBufferLength),
338333
context_(context->GetIsolate(), context) {
339334
// We'll be creating new objects so make sure we've entered the context.
340335
HandleScope handle_scope(isolate());
341336
Context::Scope context_scope(context);
342337

338+
// Set some flags if only kDefaultFlags was passed. This can make API version
339+
// transitions easier for embedders.
340+
if (flags_ & EnvironmentFlags::kDefaultFlags) {
341+
flags_ = flags_ |
342+
EnvironmentFlags::kOwnsProcessState |
343+
EnvironmentFlags::kOwnsInspector;
344+
}
345+
343346
set_env_vars(per_process::system_environment);
344347
enabled_debug_list_.Parse(this);
345348

@@ -358,6 +361,10 @@ Environment::Environment(IsolateData* isolate_data,
358361

359362
AssignToContext(context, ContextInfo(""));
360363

364+
static uv_once_t init_once = UV_ONCE_INIT;
365+
uv_once(&init_once, InitThreadLocalOnce);
366+
uv_key_set(&thread_local_env, this);
367+
361368
if (tracing::AgentWriterHandle* writer = GetTracingAgentWriter()) {
362369
trace_state_observer_ = std::make_unique<TrackingTraceStateObserver>(this);
363370
if (TracingController* tracing_controller = writer->GetTracingController())
@@ -407,6 +414,9 @@ Environment::Environment(IsolateData* isolate_data,
407414
Environment::~Environment() {
408415
if (interrupt_data_ != nullptr) *interrupt_data_ = nullptr;
409416

417+
// FreeEnvironment() should have set this.
418+
CHECK(is_stopping());
419+
410420
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
411421
BuildEmbedderGraph, this);
412422

@@ -493,6 +503,15 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
493503
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
494504
uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
495505

506+
{
507+
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
508+
task_queues_async_initialized_ = true;
509+
if (native_immediates_threadsafe_.size() > 0 ||
510+
native_immediates_interrupts_.size() > 0) {
511+
uv_async_send(&task_queues_async_);
512+
}
513+
}
514+
496515
// Register clean-up cb to be called to clean up the handles
497516
// when the environment is freed, note that they are not cleaned in
498517
// the one environment per process setup, but will be called in
@@ -502,10 +521,6 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
502521
if (start_profiler_idle_notifier) {
503522
StartProfilerIdleNotifier();
504523
}
505-
506-
static uv_once_t init_once = UV_ONCE_INIT;
507-
uv_once(&init_once, InitThreadLocalOnce);
508-
uv_key_set(&thread_local_env, this);
509524
}
510525

511526
void Environment::ExitEnv() {
@@ -539,6 +554,11 @@ void Environment::RegisterHandleCleanups() {
539554
}
540555

541556
void Environment::CleanupHandles() {
557+
{
558+
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
559+
task_queues_async_initialized_ = false;
560+
}
561+
542562
Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(),
543563
Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
544564

@@ -1103,6 +1123,7 @@ void Environment::CleanupFinalizationGroups() {
11031123
if (try_catch.HasCaught() && !try_catch.HasTerminated())
11041124
errors::TriggerUncaughtException(isolate(), try_catch);
11051125
// Re-schedule the execution of the remainder of the queue.
1126+
CHECK(task_queues_async_initialized_);
11061127
uv_async_send(&task_queues_async_);
11071128
return;
11081129
}

src/env.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -874,12 +874,6 @@ class Environment : public MemoryRetainer {
874874
inline void PushAsyncCallbackScope();
875875
inline void PopAsyncCallbackScope();
876876

877-
enum Flags {
878-
kNoFlags = 0,
879-
kOwnsProcessState = 1 << 1,
880-
kOwnsInspector = 1 << 2,
881-
};
882-
883877
static inline Environment* GetCurrent(v8::Isolate* isolate);
884878
static inline Environment* GetCurrent(v8::Local<v8::Context> context);
885879
static inline Environment* GetCurrent(
@@ -898,8 +892,8 @@ class Environment : public MemoryRetainer {
898892
v8::Local<v8::Context> context,
899893
const std::vector<std::string>& args,
900894
const std::vector<std::string>& exec_args,
901-
Flags flags = Flags(),
902-
uint64_t thread_id = kNoThreadId);
895+
EnvironmentFlags::Flags flags,
896+
ThreadId thread_id);
903897
~Environment() override;
904898

905899
void InitializeLibuv(bool start_profiler_idle_notifier);
@@ -1068,9 +1062,6 @@ class Environment : public MemoryRetainer {
10681062
inline bool has_serialized_options() const;
10691063
inline void set_has_serialized_options(bool has_serialized_options);
10701064

1071-
static uint64_t AllocateThreadId();
1072-
static constexpr uint64_t kNoThreadId = -1;
1073-
10741065
inline bool is_main_thread() const;
10751066
inline bool owns_process_state() const;
10761067
inline bool owns_inspector() const;
@@ -1350,7 +1341,7 @@ class Environment : public MemoryRetainer {
13501341
bool has_serialized_options_ = false;
13511342

13521343
std::atomic_bool can_call_into_js_ { true };
1353-
Flags flags_;
1344+
uint64_t flags_;
13541345
uint64_t thread_id_;
13551346
std::unordered_set<worker::Worker*> sub_worker_contexts_;
13561347

@@ -1409,6 +1400,11 @@ class Environment : public MemoryRetainer {
14091400
Mutex native_immediates_threadsafe_mutex_;
14101401
NativeImmediateQueue native_immediates_threadsafe_;
14111402
NativeImmediateQueue native_immediates_interrupts_;
1403+
// Also guarded by native_immediates_threadsafe_mutex_. This can be used when
1404+
// trying to post tasks from other threads to an Environment, as the libuv
1405+
// handle for the immediate queues (task_queues_async_) may not be initialized
1406+
// yet or already have been destroyed.
1407+
bool task_queues_async_initialized_ = false;
14121408

14131409
void RunAndClearNativeImmediates(bool only_refed = false);
14141410
void RunAndClearInterrupts();

0 commit comments

Comments
 (0)