mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
PR-URL: https://github.com/nodejs/node/pull/58047 Refs: https://github.com/nodejs/node/pull/47452 Refs: https://github.com/nodejs/node/issues/54918 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
239 lines
7.8 KiB
C++
239 lines
7.8 KiB
C++
#ifndef SRC_NODE_PLATFORM_H_
|
|
#define SRC_NODE_PLATFORM_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include <queue>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include <functional>
|
|
|
|
#include "libplatform/libplatform.h"
|
|
#include "node.h"
|
|
#include "node_mutex.h"
|
|
#include "uv.h"
|
|
|
|
namespace node {
|
|
|
|
class NodePlatform;
|
|
class IsolateData;
|
|
class PerIsolatePlatformData;
|
|
|
|
template <class T>
|
|
class TaskQueue {
|
|
public:
|
|
class Locked {
|
|
public:
|
|
void Push(std::unique_ptr<T> task);
|
|
std::unique_ptr<T> Pop();
|
|
std::unique_ptr<T> BlockingPop();
|
|
void NotifyOfCompletion();
|
|
void BlockingDrain();
|
|
void Stop();
|
|
std::queue<std::unique_ptr<T>> PopAll();
|
|
|
|
private:
|
|
friend class TaskQueue;
|
|
explicit Locked(TaskQueue* queue);
|
|
|
|
TaskQueue* queue_;
|
|
Mutex::ScopedLock lock_;
|
|
};
|
|
|
|
TaskQueue();
|
|
~TaskQueue() = default;
|
|
|
|
Locked Lock() { return Locked(this); }
|
|
|
|
private:
|
|
Mutex lock_;
|
|
ConditionVariable tasks_available_;
|
|
ConditionVariable tasks_drained_;
|
|
int outstanding_tasks_;
|
|
bool stopped_;
|
|
std::queue<std::unique_ptr<T>> task_queue_;
|
|
};
|
|
|
|
struct DelayedTask {
|
|
std::unique_ptr<v8::Task> task;
|
|
uv_timer_t timer;
|
|
double timeout;
|
|
std::shared_ptr<PerIsolatePlatformData> platform_data;
|
|
};
|
|
|
|
enum class PlatformDebugLogLevel {
|
|
kNone = 0,
|
|
kMinimal = 1,
|
|
kVerbose = 2,
|
|
};
|
|
|
|
// This acts as the foreground task runner for a given Isolate.
|
|
class PerIsolatePlatformData :
|
|
public IsolatePlatformDelegate,
|
|
public v8::TaskRunner,
|
|
public std::enable_shared_from_this<PerIsolatePlatformData> {
|
|
public:
|
|
PerIsolatePlatformData(
|
|
v8::Isolate* isolate,
|
|
uv_loop_t* loop,
|
|
PlatformDebugLogLevel debug_log_level = PlatformDebugLogLevel::kNone);
|
|
~PerIsolatePlatformData() override;
|
|
|
|
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner() override;
|
|
void PostTask(std::unique_ptr<v8::Task> task) override;
|
|
void PostIdleTask(std::unique_ptr<v8::IdleTask> task) override;
|
|
void PostDelayedTask(std::unique_ptr<v8::Task> task,
|
|
double delay_in_seconds) override;
|
|
bool IdleTasksEnabled() override { return false; }
|
|
|
|
// Non-nestable tasks are treated like regular tasks.
|
|
bool NonNestableTasksEnabled() const override { return true; }
|
|
bool NonNestableDelayedTasksEnabled() const override { return true; }
|
|
void PostNonNestableTask(std::unique_ptr<v8::Task> task) override;
|
|
void PostNonNestableDelayedTask(std::unique_ptr<v8::Task> task,
|
|
double delay_in_seconds) override;
|
|
|
|
void AddShutdownCallback(void (*callback)(void*), void* data);
|
|
void Shutdown();
|
|
|
|
// Returns true if work was dispatched or executed. New tasks that are
|
|
// posted during flushing of the queue are postponed until the next
|
|
// flushing.
|
|
bool FlushForegroundTasksInternal();
|
|
|
|
const uv_loop_t* event_loop() const { return loop_; }
|
|
|
|
private:
|
|
void DeleteFromScheduledTasks(DelayedTask* task);
|
|
void DecreaseHandleCount();
|
|
|
|
static void FlushTasks(uv_async_t* handle);
|
|
void RunForegroundTask(std::unique_ptr<v8::Task> task);
|
|
static void RunForegroundTask(uv_timer_t* timer);
|
|
|
|
uv_async_t* flush_tasks_ = nullptr;
|
|
|
|
struct ShutdownCallback {
|
|
void (*cb)(void*);
|
|
void* data;
|
|
};
|
|
typedef std::vector<ShutdownCallback> ShutdownCbList;
|
|
ShutdownCbList shutdown_callbacks_;
|
|
// shared_ptr to self to keep this object alive during shutdown.
|
|
std::shared_ptr<PerIsolatePlatformData> self_reference_;
|
|
uint32_t uv_handle_count_ = 1; // 1 = flush_tasks_
|
|
|
|
v8::Isolate* const isolate_;
|
|
uv_loop_t* const loop_;
|
|
|
|
// When acquiring locks for both task queues, lock foreground_tasks_
|
|
// first then foreground_delayed_tasks_ to avoid deadlocks.
|
|
TaskQueue<v8::Task> foreground_tasks_;
|
|
TaskQueue<DelayedTask> foreground_delayed_tasks_;
|
|
|
|
// Use a custom deleter because libuv needs to close the handle first.
|
|
typedef std::unique_ptr<DelayedTask, void(*)(DelayedTask*)>
|
|
DelayedTaskPointer;
|
|
std::vector<DelayedTaskPointer> scheduled_delayed_tasks_;
|
|
PlatformDebugLogLevel debug_log_level_ = PlatformDebugLogLevel::kNone;
|
|
};
|
|
|
|
// This acts as the single worker thread task runner for all Isolates.
|
|
class WorkerThreadsTaskRunner {
|
|
public:
|
|
explicit WorkerThreadsTaskRunner(int thread_pool_size,
|
|
PlatformDebugLogLevel debug_log_level);
|
|
|
|
void PostTask(std::unique_ptr<v8::Task> task);
|
|
void PostDelayedTask(std::unique_ptr<v8::Task> task,
|
|
double delay_in_seconds);
|
|
|
|
void BlockingDrain();
|
|
void Shutdown();
|
|
|
|
int NumberOfWorkerThreads() const;
|
|
|
|
private:
|
|
// A queue shared by all threads. The consumers are the worker threads which
|
|
// take tasks from it to run in PlatformWorkerThread(). The producers can be
|
|
// any thread. Both the foreground thread and the worker threads can push
|
|
// tasks into the queue via v8::Platform::PostTaskOnWorkerThread() which
|
|
// eventually calls PostTask() on this class. When any thread calls
|
|
// v8::Platform::PostDelayedTaskOnWorkerThread(), the DelayedTaskScheduler
|
|
// thread will schedule a timer that pushes the delayed tasks back into this
|
|
// queue when the timer expires.
|
|
TaskQueue<v8::Task> pending_worker_tasks_;
|
|
|
|
class DelayedTaskScheduler;
|
|
std::unique_ptr<DelayedTaskScheduler> delayed_task_scheduler_;
|
|
|
|
std::vector<std::unique_ptr<uv_thread_t>> threads_;
|
|
PlatformDebugLogLevel debug_log_level_ = PlatformDebugLogLevel::kNone;
|
|
};
|
|
|
|
class NodePlatform : public MultiIsolatePlatform {
|
|
public:
|
|
NodePlatform(int thread_pool_size,
|
|
v8::TracingController* tracing_controller,
|
|
v8::PageAllocator* page_allocator = nullptr);
|
|
~NodePlatform() override;
|
|
|
|
void DrainTasks(v8::Isolate* isolate) override;
|
|
void Shutdown();
|
|
|
|
// v8::Platform implementation.
|
|
int NumberOfWorkerThreads() override;
|
|
void PostTaskOnWorkerThreadImpl(v8::TaskPriority priority,
|
|
std::unique_ptr<v8::Task> task,
|
|
const v8::SourceLocation& location) override;
|
|
void PostDelayedTaskOnWorkerThreadImpl(
|
|
v8::TaskPriority priority,
|
|
std::unique_ptr<v8::Task> task,
|
|
double delay_in_seconds,
|
|
const v8::SourceLocation& location) override;
|
|
bool IdleTasksEnabled(v8::Isolate* isolate) override;
|
|
double MonotonicallyIncreasingTime() override;
|
|
double CurrentClockTimeMillis() override;
|
|
v8::TracingController* GetTracingController() override;
|
|
bool FlushForegroundTasks(v8::Isolate* isolate) override;
|
|
std::unique_ptr<v8::JobHandle> CreateJobImpl(
|
|
v8::TaskPriority priority,
|
|
std::unique_ptr<v8::JobTask> job_task,
|
|
const v8::SourceLocation& location) override;
|
|
|
|
void RegisterIsolate(v8::Isolate* isolate, uv_loop_t* loop) override;
|
|
void RegisterIsolate(v8::Isolate* isolate,
|
|
IsolatePlatformDelegate* delegate) override;
|
|
|
|
void UnregisterIsolate(v8::Isolate* isolate) override;
|
|
void AddIsolateFinishedCallback(v8::Isolate* isolate,
|
|
void (*callback)(void*), void* data) override;
|
|
|
|
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
|
|
v8::Isolate* isolate) override;
|
|
|
|
Platform::StackTracePrinter GetStackTracePrinter() override;
|
|
v8::PageAllocator* GetPageAllocator() override;
|
|
|
|
private:
|
|
IsolatePlatformDelegate* ForIsolate(v8::Isolate* isolate);
|
|
std::shared_ptr<PerIsolatePlatformData> ForNodeIsolate(v8::Isolate* isolate);
|
|
|
|
Mutex per_isolate_mutex_;
|
|
using DelegatePair = std::pair<
|
|
IsolatePlatformDelegate*, std::shared_ptr<PerIsolatePlatformData>>;
|
|
std::unordered_map<v8::Isolate*, DelegatePair> per_isolate_;
|
|
|
|
v8::TracingController* tracing_controller_;
|
|
v8::PageAllocator* page_allocator_;
|
|
std::shared_ptr<WorkerThreadsTaskRunner> worker_thread_task_runner_;
|
|
bool has_shut_down_ = false;
|
|
PlatformDebugLogLevel debug_log_level_ = PlatformDebugLogLevel::kNone;
|
|
};
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_PLATFORM_H_
|