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