1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef INCLUDE_CPPGC_PLATFORM_H_ 6#define INCLUDE_CPPGC_PLATFORM_H_ 7 8#include <memory> 9 10#include "cppgc/source-location.h" 11#include "v8-platform.h" // NOLINT(build/include_directory) 12#include "v8config.h" // NOLINT(build/include_directory) 13 14namespace cppgc { 15 16// TODO(v8:10346): Create separate includes for concepts that are not 17// V8-specific. 18using IdleTask = v8::IdleTask; 19using JobHandle = v8::JobHandle; 20using JobDelegate = v8::JobDelegate; 21using JobTask = v8::JobTask; 22using PageAllocator = v8::PageAllocator; 23using Task = v8::Task; 24using TaskPriority = v8::TaskPriority; 25using TaskRunner = v8::TaskRunner; 26using TracingController = v8::TracingController; 27 28/** 29 * Platform interface used by Heap. Contains allocators and executors. 30 */ 31class V8_EXPORT Platform { 32 public: 33 virtual ~Platform() = default; 34 35 /** 36 * \returns the allocator used by cppgc to allocate its heap and various 37 * support structures. Returning nullptr results in using the `PageAllocator` 38 * provided by `cppgc::InitializeProcess()` instead. 39 */ 40 virtual PageAllocator* GetPageAllocator() = 0; 41 42 /** 43 * Monotonically increasing time in seconds from an arbitrary fixed point in 44 * the past. This function is expected to return at least 45 * millisecond-precision values. For this reason, 46 * it is recommended that the fixed point be no further in the past than 47 * the epoch. 48 **/ 49 virtual double MonotonicallyIncreasingTime() = 0; 50 51 /** 52 * Foreground task runner that should be used by a Heap. 53 */ 54 virtual std::shared_ptr<TaskRunner> GetForegroundTaskRunner() { 55 return nullptr; 56 } 57 58 /** 59 * Posts `job_task` to run in parallel. Returns a `JobHandle` associated with 60 * the `Job`, which can be joined or canceled. 61 * This avoids degenerate cases: 62 * - Calling `CallOnWorkerThread()` for each work item, causing significant 63 * overhead. 64 * - Fixed number of `CallOnWorkerThread()` calls that split the work and 65 * might run for a long time. This is problematic when many components post 66 * "num cores" tasks and all expect to use all the cores. In these cases, 67 * the scheduler lacks context to be fair to multiple same-priority requests 68 * and/or ability to request lower priority work to yield when high priority 69 * work comes in. 70 * A canonical implementation of `job_task` looks like: 71 * \code 72 * class MyJobTask : public JobTask { 73 * public: 74 * MyJobTask(...) : worker_queue_(...) {} 75 * // JobTask implementation. 76 * void Run(JobDelegate* delegate) override { 77 * while (!delegate->ShouldYield()) { 78 * // Smallest unit of work. 79 * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe. 80 * if (!work_item) return; 81 * ProcessWork(work_item); 82 * } 83 * } 84 * 85 * size_t GetMaxConcurrency() const override { 86 * return worker_queue_.GetSize(); // Thread safe. 87 * } 88 * }; 89 * 90 * // ... 91 * auto handle = PostJob(TaskPriority::kUserVisible, 92 * std::make_unique<MyJobTask>(...)); 93 * handle->Join(); 94 * \endcode 95 * 96 * `PostJob()` and methods of the returned JobHandle/JobDelegate, must never 97 * be called while holding a lock that could be acquired by `JobTask::Run()` 98 * or `JobTask::GetMaxConcurrency()` -- that could result in a deadlock. This 99 * is because (1) `JobTask::GetMaxConcurrency()` may be invoked while holding 100 * internal lock (A), hence `JobTask::GetMaxConcurrency()` can only use a lock 101 * (B) if that lock is *never* held while calling back into `JobHandle` from 102 * any thread (A=>B/B=>A deadlock) and (2) `JobTask::Run()` or 103 * `JobTask::GetMaxConcurrency()` may be invoked synchronously from 104 * `JobHandle` (B=>JobHandle::foo=>B deadlock). 105 * 106 * A sufficient `PostJob()` implementation that uses the default Job provided 107 * in libplatform looks like: 108 * \code 109 * std::unique_ptr<JobHandle> PostJob( 110 * TaskPriority priority, std::unique_ptr<JobTask> job_task) override { 111 * return std::make_unique<DefaultJobHandle>( 112 * std::make_shared<DefaultJobState>( 113 * this, std::move(job_task), kNumThreads)); 114 * } 115 * \endcode 116 */ 117 virtual std::unique_ptr<JobHandle> PostJob( 118 TaskPriority priority, std::unique_ptr<JobTask> job_task) { 119 return nullptr; 120 } 121 122 /** 123 * Returns an instance of a `TracingController`. This must be non-nullptr. The 124 * default implementation returns an empty `TracingController` that consumes 125 * trace data without effect. 126 */ 127 virtual TracingController* GetTracingController(); 128}; 129 130/** 131 * Process-global initialization of the garbage collector. Must be called before 132 * creating a Heap. 133 * 134 * Can be called multiple times when paired with `ShutdownProcess()`. 135 * 136 * \param page_allocator The allocator used for maintaining meta data. Must stay 137 * always alive and not change between multiple calls to InitializeProcess. If 138 * no allocator is provided, a default internal version will be used. 139 */ 140V8_EXPORT void InitializeProcess(PageAllocator* page_allocator = nullptr); 141 142/** 143 * Must be called after destroying the last used heap. Some process-global 144 * metadata may not be returned and reused upon a subsequent 145 * `InitializeProcess()` call. 146 */ 147V8_EXPORT void ShutdownProcess(); 148 149namespace internal { 150 151V8_EXPORT void Fatal(const std::string& reason = std::string(), 152 const SourceLocation& = SourceLocation::Current()); 153 154} // namespace internal 155 156} // namespace cppgc 157 158#endif // INCLUDE_CPPGC_PLATFORM_H_ 159