Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ebdf84a
src: make `FreeEnvironment()` perform all necessary cleanup
addaleax Nov 10, 2019
3f9fa28
src: fix memory leak in CreateEnvironment when bootstrap fails
addaleax Nov 11, 2019
459973c
src: move worker_context from Environment to IsolateData
addaleax Nov 12, 2019
2c87b17
src: associate is_main_thread() with worker_context()
addaleax Nov 12, 2019
0e9f4a9
src: align worker and main thread code with embedder API
addaleax Nov 11, 2019
c694f08
src: provide a variant of LoadEnvironment taking a callback
addaleax Nov 13, 2019
8a1255c
src: add LoadEnvironment() variant taking a string
addaleax Nov 19, 2019
bb689cf
test: re-enable cctest that was commented out
addaleax Feb 27, 2020
ad95b85
src: add unique_ptr equivalent of CreatePlatform
addaleax Feb 28, 2020
33ecd3b
src: make InitializeNodeWithArgs() official public API
addaleax Feb 28, 2020
31528f3
src: add ability to look up platform based on `Environment*`
addaleax Feb 28, 2020
eca25d0
src: allow non-Node.js TracingControllers
addaleax Feb 28, 2020
691a575
src: fix what a dispose without checking
Jan 8, 2020
1c5d55a
src: shutdown platform from FreePlatform()
addaleax Feb 28, 2020
a02c5d0
src,test: add full-featured embedder API test
addaleax Feb 28, 2020
0547551
doc: add basic embedding example documentation
addaleax Feb 28, 2020
a7ada43
test: add extended embedder cctest
addaleax Mar 12, 2020
1a8e9ee
test: wait for message from parent in embedding cctest
addaleax Mar 30, 2020
6d231db
embedding: provide hook for custom process.exit() behaviour
addaleax Mar 28, 2020
93a23b9
embedding: make Stop() stop Workers
addaleax Mar 29, 2020
72f42bf
test: use InitializeNodeWithArgs in cctest
addaleax Mar 21, 2020
b931e68
test: use common.buildType in embedding test
addaleax Mar 22, 2020
9aa4cbe
src: initialize inspector before RunBootstrapping()
addaleax Apr 5, 2020
0a2f5e9
worker: unify custom error creation
addaleax Apr 27, 2020
b910fc6
fixup! src: run native immediates during Environment cleanup
addaleax Sep 17, 2020
1ee495d
fixup! test: add extended embedder cctest
addaleax Sep 17, 2020
30b6c2f
fixup! src,test: add full-featured embedder API test
addaleax Sep 17, 2020
6e63159
fixup! doc: add basic embedding example documentation
addaleax Sep 17, 2020
8f464fe
src: make `Environment::interrupt_data_` atomic
addaleax Mar 22, 2020
a74f870
src: fix cleanup hook removal for InspectorTimer
addaleax Mar 27, 2020
a2ebe11
src: use env->RequestInterrupt() for inspector io thread start
addaleax Mar 22, 2020
83015b0
src: use env->RequestInterrupt() for inspector MainThreadInterface
addaleax Mar 22, 2020
2b0315f
src: flush V8 interrupts from Environment dtor
addaleax Mar 28, 2020
aa3e118
src: add callback scope for native immediates
addaleax Jul 14, 2020
c386bd1
fixup! src: add callback scope for native immediates
addaleax Sep 17, 2020
b7f9f3d
fixup! src: align worker and main thread code with embedder API
addaleax Sep 17, 2020
fa4439d
test,doc: add missing uv_setup_args() calls
cjihrig Aug 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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.

PR-URL: #30467
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
  • Loading branch information
addaleax committed Sep 23, 2020
commit 0e9f4a95ffbe2127fb28aaf3e6b9de5580239cf2
88 changes: 82 additions & 6 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<std::string>(argv, argv + argc),
std::vector<std::string>(exec_argv, exec_argv + exec_argc));
}

Environment* CreateEnvironment(
IsolateData* isolate_data,
Local<Context> context,
const std::vector<std::string>& args,
const std::vector<std::string>& 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<std::string> args(argv, argv + argc);
std::vector<std::string> 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::Flags>(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;
}

Expand All @@ -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<inspector::ParentInspectorHandle> impl;

explicit InspectorParentHandleImpl(
std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
: impl(std::move(impl)) {}
};
#endif

NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
Environment* env,
ThreadId thread_id,
const char* url) {
CHECK_NOT_NULL(env);
CHECK_NE(thread_id.id, static_cast<uint64_t>(-1));
#if HAVE_INSPECTOR
return std::make_unique<InspectorParentHandleImpl>(
env->inspector_agent()->GetParentHandle(thread_id.id, url));
#else
return {};
#endif
}

void LoadEnvironment(Environment* env) {
USE(LoadEnvironment(env, {}));
}

MaybeLocal<Value> LoadEnvironment(
Environment* env,
std::unique_ptr<InspectorParentHandle> 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<InspectorParentHandleImpl*>(
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> context) {
return Environment::GetCurrent(context);
}
Expand Down Expand Up @@ -579,4 +649,10 @@ void AddLinkedBinding(Environment* env,
AddLinkedBinding(env, mod);
}

static std::atomic<uint64_t> next_thread_id{0};

ThreadId AllocateEnvironmentThreadId() {
return ThreadId { next_thread_id++ };
}

} // namespace node
11 changes: 7 additions & 4 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename Fn>
Expand All @@ -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();
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1176,6 +1178,7 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) {
inline void Environment::RegisterFinalizationGroupForCleanup(
v8::Local<v8::FinalizationGroup> group) {
cleanup_finalization_groups_.emplace_back(isolate(), group);
DCHECK(task_queues_async_initialized_);
uv_async_send(&task_queues_async_);
}

Expand Down
47 changes: 34 additions & 13 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,6 @@ void TrackingTraceStateObserver::UpdateTraceCategoryState() {
USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args));
}

static std::atomic<uint64_t> next_thread_id{0};

uint64_t Environment::AllocateThreadId() {
return next_thread_id++;
}

void Environment::CreateProperties() {
HandleScope handle_scope(isolate_);
Local<Context> ctx = context();
Expand Down Expand Up @@ -319,8 +313,8 @@ Environment::Environment(IsolateData* isolate_data,
Local<Context> context,
const std::vector<std::string>& args,
const std::vector<std::string>& 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()),
Expand All @@ -332,14 +326,23 @@ 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<uint64_t>(-1) ?
AllocateEnvironmentThreadId().id : thread_id.id),
fs_stats_field_array_(isolate_, kFsStatsBufferLength),
fs_stats_field_bigint_array_(isolate_, kFsStatsBufferLength),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
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);

Expand All @@ -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<TrackingTraceStateObserver>(this);
if (TracingController* tracing_controller = writer->GetTracingController())
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -493,6 +503,15 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
uv_unref(reinterpret_cast<uv_handle_t*>(&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
Expand All @@ -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() {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down
20 changes: 8 additions & 12 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<v8::Context> context);
static inline Environment* GetCurrent(
Expand All @@ -898,8 +892,8 @@ class Environment : public MemoryRetainer {
v8::Local<v8::Context> context,
const std::vector<std::string>& args,
const std::vector<std::string>& 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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<worker::Worker*> sub_worker_contexts_;

Expand Down Expand Up @@ -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();
Expand Down
Loading