Skip to content

Commit

Permalink
Implement IsolatePlatformDelegate API
Browse files Browse the repository at this point in the history
This is the solution for the issue raised in #132 and discussed in #133.
Implements the API which landed in nodejs c712fb7c. We should be able to
take advantage of this starting in nodejs 14.x
  • Loading branch information
laverdet committed Dec 7, 2019
1 parent 4f1bd0d commit 7087645
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 191 deletions.
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
'src/isolate/executor.cc',
'src/isolate/holder.cc',
'src/isolate/inspector.cc',
'src/isolate/platform_delegate.cc',
'src/isolate/scheduler.cc',
'src/isolate/stack_trace.cc',
'src/isolate/three_phase_task.cc',
Expand Down
21 changes: 5 additions & 16 deletions src/isolate/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ void IsolateEnvironment::HeapCheck::Epilogue() {
* IsolateEnvironment implementation
*/
size_t IsolateEnvironment::specifics_count = 0;
std::shared_ptr<IsolateEnvironment::IsolateMap> IsolateEnvironment::isolate_map_shared = std::make_shared<IsolateMap>();

void IsolateEnvironment::OOMErrorCallback(const char* location, bool is_heap_oom) {
fprintf(stderr, "%s\nis_heap_oom = %d\n\n\n", location, static_cast<int>(is_heap_oom));
Expand Down Expand Up @@ -261,8 +260,7 @@ void IsolateEnvironment::InterruptEntrySync() {
IsolateEnvironment::IsolateEnvironment() :
owned_isolates{std::make_unique<OwnedIsolates>()},
scheduler{*this},
executor{*this},
isolate_map{isolate_map_shared} {
executor{*this} {

}

Expand Down Expand Up @@ -302,13 +300,14 @@ void IsolateEnvironment::IsolateCtor(size_t memory_limit_in_mb, shared_ptr<void>
startup_data.data = reinterpret_cast<char*>(snapshot_blob_ptr.get());
startup_data.raw_size = snapshot_length;
}
task_runner = std::make_shared<IsolateTaskRunner>(holder.lock()->GetIsolate());
#if V8_AT_LEAST(6, 8, 57)
isolate = Isolate::Allocate();
isolate_map->write()->insert(std::make_pair(isolate, this));
PlatformDelegate::RegisterIsolate(isolate, &scheduler);
Isolate::Initialize(isolate, create_params);
#else
isolate = Isolate::New(create_params);
isolate_map->write()->insert(std::make_pair(isolate, this));
PlatformDelegate::RegisterIsolate(isolate, &scheduler);
#endif
// Workaround for bug in snapshot deserializer in v8 in nodejs v10.x
isolate->SetHostImportModuleDynamicallyCallback(nullptr);
Expand Down Expand Up @@ -392,7 +391,7 @@ IsolateEnvironment::~IsolateEnvironment() {
isolate->Dispose();
}
// Unregister from Platform
isolate_map->write()->erase(isolate);
PlatformDelegate::UnregisterIsolate(isolate);
// Unreference from default isolate
executor.default_executor.env.owned_isolates->write()->erase(holder);
}
Expand Down Expand Up @@ -495,14 +494,4 @@ void IsolateEnvironment::RemoveWeakCallback(Persistent<Object>* handle) {
weak_persistents.erase(it);
}

shared_ptr<IsolateHolder> IsolateEnvironment::LookupIsolate(Isolate* isolate) {
auto lock = isolate_map_shared->read();
auto it = lock->find(isolate);
if (it == lock->end()) {
return nullptr;
} else {
return it->second->holder.lock();
}
}

} // namespace ivm
21 changes: 12 additions & 9 deletions src/isolate/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ class IsolateEnvironment {
};

private:
// IsolateMap is stored in a shared_ptr to ensure access to instances while the module is being destroyed.
using IsolateMap = lockable_t<std::unordered_map<v8::Isolate*, IsolateEnvironment*>, true>;
static std::shared_ptr<IsolateMap> isolate_map_shared;
template <class Type>
struct WeakPtrCompare {
bool operator()(const std::weak_ptr<Type>& left, const std::weak_ptr<Type>& right) const {
Expand All @@ -122,6 +119,7 @@ class IsolateEnvironment {
Scheduler scheduler;
Executor executor;
std::weak_ptr<IsolateHolder> holder;
std::shared_ptr<IsolateTaskRunner> task_runner;
std::unique_ptr<class InspectorAgent> inspector_agent;
v8::Persistent<v8::Context> default_context;
std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_ptr;
Expand All @@ -138,7 +136,6 @@ class IsolateEnvironment {
bool nodejs_isolate;
std::atomic<unsigned int> remotes_count{0};
v8::HeapStatistics last_heap {};
std::shared_ptr<IsolateMap> isolate_map;
v8::Persistent<v8::Value> rejected_promise_error;

std::vector<std::unique_ptr<v8::Eternal<v8::Data>>> specifics;
Expand Down Expand Up @@ -217,6 +214,14 @@ class IsolateEnvironment {
return Executor::GetCurrentEnvironment()->holder.lock();
}

auto GetScheduler() -> Scheduler& {
return scheduler;
}

auto GetTaskRunner() -> const std::shared_ptr<IsolateTaskRunner>& {
return task_runner;
}

/**
* Convenience operators to work with underlying isolate
*/
Expand Down Expand Up @@ -285,6 +290,9 @@ class IsolateEnvironment {
*/
InspectorAgent* GetInspectorAgent() const;

// Return IsolateHolder
auto GetHolder() { return holder; }

/**
* Check memory limit flag
*/
Expand Down Expand Up @@ -340,11 +348,6 @@ class IsolateEnvironment {
*/
void AddWeakCallback(v8::Persistent<v8::Object>* handle, void(*fn)(void*), void* param);
void RemoveWeakCallback(v8::Persistent<v8::Object>* handle);

/**
* Given a v8 isolate this will find the IsolateEnvironment instance, if any, that belongs to it.
*/
static std::shared_ptr<IsolateHolder> LookupIsolate(v8::Isolate* isolate);
};

} // namespace ivm
47 changes: 42 additions & 5 deletions src/isolate/holder.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#include "holder.h"
#include "environment.h"
#include "scheduler.h"
#include "util.h"
#include "../lib/timer.h"
#include <utility>

using std::shared_ptr;
using std::unique_ptr;

namespace ivm {

auto IsolateHolder::Dispose() -> bool {
Expand All @@ -28,11 +27,11 @@ void IsolateHolder::ReleaseAndJoin() {
}
}

shared_ptr<IsolateEnvironment> IsolateHolder::GetIsolate() {
auto IsolateHolder::GetIsolate() -> std::shared_ptr<IsolateEnvironment> {
return state.read()->isolate;
}

void IsolateHolder::ScheduleTask(unique_ptr<Runnable> task, bool run_inline, bool wake_isolate, bool handle_task) {
void IsolateHolder::ScheduleTask(std::unique_ptr<Runnable> task, bool run_inline, bool wake_isolate, bool handle_task) {
auto ref = state.read()->isolate;
if (ref) {
if (run_inline && IsolateEnvironment::GetCurrent() == ref.get()) {
Expand All @@ -51,4 +50,42 @@ void IsolateHolder::ScheduleTask(unique_ptr<Runnable> task, bool run_inline, boo
}
}

// Methods for v8::TaskRunner
void IsolateTaskRunner::PostTask(std::unique_ptr<v8::Task> task) {
auto env = weak_env.lock();
if (env) {
if (IsolateEnvironment::GetCurrent() == env.get()) {
task->Run();
return;
} else {
Scheduler::Lock lock{env->GetScheduler()};
lock.scheduler.tasks.push(std::move(task));
}
}
}

void IsolateTaskRunner::PostNonNestableTask(std::unique_ptr<v8::Task> task) {
auto env = weak_env.lock();
if (env) {
Scheduler::Lock lock{env->GetScheduler()};
lock.scheduler.tasks.push(std::move(task));
}
}

void IsolateTaskRunner::PostDelayedTask(std::unique_ptr<v8::Task> task, double delay_in_seconds) {
// wait_detached erases the type of the lambda into a std::function which must be
// copyable. The unique_ptr is stored in a shared pointer so ownership can be handled correctly.
auto shared_task = std::make_shared<std::unique_ptr<v8::Task>>(std::move(task));
auto weak_env = this->weak_env;
timer_t::wait_detached(static_cast<uint32_t>(delay_in_seconds * 1000), [shared_task, weak_env](void* next) {
auto env = weak_env.lock();
if (env) {
// Don't wake the isolate, this will just run the next time the isolate is doing something
Scheduler::Lock lock{env->GetScheduler()};
lock.scheduler.tasks.push(std::move(*shared_task));
}
timer_t::chain(next);
});
}

} // namespace ivm
23 changes: 22 additions & 1 deletion src/isolate/holder.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#pragma once
#include "platform_delegate.h"
#include "runnable.h"
#include "v8_version.h"
#include "../lib/lockable.h"
#include <v8-platform.h>
#include <condition_variable>
#include <mutex>
#include <memory>
Expand All @@ -19,7 +22,7 @@ class IsolateHolder {

auto Dispose() -> bool;
void ReleaseAndJoin();
std::shared_ptr<IsolateEnvironment> GetIsolate();
auto GetIsolate() -> std::shared_ptr<IsolateEnvironment>;
void ScheduleTask(std::unique_ptr<Runnable> task, bool run_inline, bool wake_isolate, bool handle_task = false);

private:
Expand All @@ -33,4 +36,22 @@ class IsolateHolder {
LockableState::condition_variable_t cv;
};

// This needs to be separate from IsolateHolder because v8 holds references to this indefinitely and
// we don't want it keeping the isolate alive.
class IsolateTaskRunner : public TaskRunner {
public:
explicit IsolateTaskRunner(const std::shared_ptr<IsolateEnvironment>& isolate) : weak_env{isolate} {}
IsolateTaskRunner(const IsolateTaskRunner&) = delete;
~IsolateTaskRunner() final = default;
auto operator=(const IsolateTaskRunner&) = delete;

// Methods for v8::TaskRunner
void PostTask(std::unique_ptr<v8::Task> task) final;
void PostNonNestableTask(std::unique_ptr<v8::Task> task) final;
void PostDelayedTask(std::unique_ptr<v8::Task> task, double delay_in_seconds) final;

private:
std::weak_ptr<IsolateEnvironment> weak_env;
};

} // namespace ivm
43 changes: 43 additions & 0 deletions src/isolate/platform_delegate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "platform_delegate.h"

namespace ivm {
namespace {
PlatformDelegate delegate;
}
#if IVM_USE_PLATFORM_DELEGATE_HACK

void PlatformDelegate::InitializeDelegate() {
auto node_platform = reinterpret_cast<node::MultiIsolatePlatform*>(v8::internal::V8::GetCurrentPlatform());
delegate = PlatformDelegate{node_platform};
v8::V8::ShutdownPlatform();
v8::V8::InitializePlatform(&delegate);
}

void PlatformDelegate::RegisterIsolate(v8::Isolate* isolate, IsolatePlatformDelegate* isolate_delegate) {
auto result = delegate.isolate_map.write()->insert(std::make_pair(isolate, isolate_delegate));
assert(result.second);
}

void PlatformDelegate::UnregisterIsolate(v8::Isolate* isolate) {
auto result = delegate.isolate_map.write()->erase(isolate);
assert(result == 1);
}

#else

void PlatformDelegate::InitializeDelegate() {
auto node_platform = node::GetMainThreadMultiIsolatePlatform();
delegate = PlatformDelegate{node_platform};
}

void PlatformDelegate::RegisterIsolate(v8::Isolate* isolate, IsolatePlatformDelegate* isolate_delegate) {
delegate.node_platform->RegisterIsolate(isolate, isolate_delegate);
}

void PlatformDelegate::UnregisterIsolate(v8::Isolate* isolate) {
delegate.node_platform->UnregisterIsolate(isolate);
}

#endif

} // namespace ivm
Loading

0 comments on commit 7087645

Please sign in to comment.