diff --git a/ext/mini_racer_extension/mini_racer_extension.cc b/ext/mini_racer_extension/mini_racer_extension.cc index 2f167fd..9b4d298 100644 --- a/ext/mini_racer_extension/mini_racer_extension.cc +++ b/ext/mini_racer_extension/mini_racer_extension.cc @@ -3,9 +3,14 @@ #include #include #include +// v8::Platform has a number of protected abstract virtual methods that +// implementers must implement. We want to delegate to DefaultPlatform +// as much as possible, ergo, we must have access to said methods. +#define protected public +#include +#undef protected #include #include -#include #include #include #include @@ -24,6 +29,132 @@ using namespace v8; +// Delegate everything to DefaultPlatform with the exception of +// ThreadIsolatedAllocator, to work around a V8 bug. +class MiniRacerPlatform : public Platform { +public: + explicit MiniRacerPlatform(bool single_threaded) { + if (single_threaded) { + platform = platform::NewSingleThreadedDefaultPlatform(); + } else { + platform = platform::NewDefaultPlatform(); + } + } + + PageAllocator* GetPageAllocator() final { + return platform->GetPageAllocator(); + } + + // V8's default thread-isolated allocator has a bug on x64 Linux. + // + // It uses memory protection keys (see `man 7 pkeys`) to + // write-protect JIT code memory but in a way that is currently + // incompatible with how we use threads. + // + // Specifically, pkey permissions are inherited by child threads. + // Threads that are not descendants of the thread that allocates + // the pkey default to "no permissions" for that pkey. + // + // Concretely, if thread A creates the v8::Platform (and the pkey) + // and write-protects memory, then later thread B tries to access + // that memory, it segfaults due to the lack of permissions. + // + // The fix on V8's side is conceptually easy - call + // pkey_set(PKEY_DISABLE_WRITE) before accessing the memory, + // to flip the permissions from "none" to "can read" - but + // until it's actually fixed, disable thread-isolation. + // + // See https://issues.chromium.org/issues/360909072 + ThreadIsolatedAllocator* GetThreadIsolatedAllocator() final { + return nullptr; + } + + ZoneBackingAllocator* GetZoneBackingAllocator() final { + return platform->GetZoneBackingAllocator(); + } + + void OnCriticalMemoryPressure() final { + return platform->OnCriticalMemoryPressure(); + } + + int NumberOfWorkerThreads() final { + return platform->NumberOfWorkerThreads(); + } + + std::shared_ptr GetForegroundTaskRunner(Isolate* isolate) { + return platform->GetForegroundTaskRunner(isolate); + } + + std::shared_ptr GetForegroundTaskRunner( + Isolate* isolate, TaskPriority priority) final { + return platform->GetForegroundTaskRunner(isolate, priority); + } + + bool IdleTasksEnabled(Isolate* isolate) final { + return platform->IdleTasksEnabled(isolate); + } + + std::unique_ptr CreateBlockingScope( + BlockingType blocking_type) final { + return platform->CreateBlockingScope(blocking_type); + } + + double MonotonicallyIncreasingTime() final { + return platform->MonotonicallyIncreasingTime(); + } + + int64_t CurrentClockTimeMilliseconds() final { + return platform->CurrentClockTimeMilliseconds(); + } + + double CurrentClockTimeMillis() final { + return platform->CurrentClockTimeMillis(); + } + + double CurrentClockTimeMillisecondsHighResolution() final { + return platform->CurrentClockTimeMillisecondsHighResolution(); + } + + StackTracePrinter GetStackTracePrinter() final { + return platform->GetStackTracePrinter(); + } + + TracingController* GetTracingController() final { + return platform->GetTracingController(); + } + + void DumpWithoutCrashing() final { + return platform->DumpWithoutCrashing(); + } + + HighAllocationThroughputObserver* GetHighAllocationThroughputObserver() + final { + return platform->GetHighAllocationThroughputObserver(); + } + + std::unique_ptr CreateJobImpl( + TaskPriority priority, std::unique_ptr job_task, + const SourceLocation& location) final { + return platform->CreateJobImpl(priority, std::move(job_task), location); + } + + void PostTaskOnWorkerThreadImpl( + TaskPriority priority, std::unique_ptr task, + const SourceLocation& location) final { + return platform->PostTaskOnWorkerThreadImpl( + priority, std::move(task), location); + } + + void PostDelayedTaskOnWorkerThreadImpl( + TaskPriority priority, std::unique_ptr task, + double delay_in_seconds, const SourceLocation& location) final { + return platform->PostDelayedTaskOnWorkerThreadImpl( + priority, std::move(task), delay_in_seconds, location); + } + + std::unique_ptr platform; +}; + typedef struct { const char* data; int raw_size; @@ -365,11 +496,7 @@ static void init_v8() { if (current_platform == NULL) { V8::InitializeICU(); - if (single_threaded) { - current_platform = platform::NewSingleThreadedDefaultPlatform(); - } else { - current_platform = platform::NewDefaultPlatform(); - } + current_platform.reset(new MiniRacerPlatform(single_threaded)); V8::InitializePlatform(current_platform.get()); V8::Initialize(); } diff --git a/lib/mini_racer/version.rb b/lib/mini_racer/version.rb index f1355fc..44422c3 100644 --- a/lib/mini_racer/version.rb +++ b/lib/mini_racer/version.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module MiniRacer - VERSION = "0.14.1" - LIBV8_NODE_VERSION = "~> 18.19.0.0" + VERSION = "0.15.0" + LIBV8_NODE_VERSION = "~> 22.5.1.0" end