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