1// Copyright 2013 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#include "src/libplatform/default-platform.h" 6 7#include <algorithm> 8#include <queue> 9 10#include "include/libplatform/libplatform.h" 11#include "src/base/bounded-page-allocator.h" 12#include "src/base/debug/stack_trace.h" 13#include "src/base/logging.h" 14#include "src/base/page-allocator.h" 15#include "src/base/platform/platform.h" 16#include "src/base/platform/time.h" 17#include "src/base/sys-info.h" 18#include "src/libplatform/default-foreground-task-runner.h" 19#include "src/libplatform/default-job.h" 20#include "src/libplatform/default-worker-threads-task-runner.h" 21 22namespace v8 { 23namespace platform { 24 25namespace { 26 27void PrintStackTrace() { 28 v8::base::debug::StackTrace trace; 29 trace.Print(); 30 // Avoid dumping duplicate stack trace on abort signal. 31 v8::base::debug::DisableSignalStackDump(); 32} 33 34constexpr int kMaxThreadPoolSize = 16; 35 36int GetActualThreadPoolSize(int thread_pool_size) { 37 DCHECK_GE(thread_pool_size, 0); 38 if (thread_pool_size < 1) { 39 thread_pool_size = base::SysInfo::NumberOfProcessors() - 1; 40 } 41 return std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1); 42} 43 44} // namespace 45 46std::unique_ptr<v8::Platform> NewDefaultPlatform( 47 int thread_pool_size, IdleTaskSupport idle_task_support, 48 InProcessStackDumping in_process_stack_dumping, 49 std::unique_ptr<v8::TracingController> tracing_controller) { 50 if (in_process_stack_dumping == InProcessStackDumping::kEnabled) { 51 v8::base::debug::EnableInProcessStackDumping(); 52 } 53 thread_pool_size = GetActualThreadPoolSize(thread_pool_size); 54 auto platform = std::make_unique<DefaultPlatform>( 55 thread_pool_size, idle_task_support, std::move(tracing_controller)); 56 return platform; 57} 58 59std::unique_ptr<v8::Platform> NewSingleThreadedDefaultPlatform( 60 IdleTaskSupport idle_task_support, 61 InProcessStackDumping in_process_stack_dumping, 62 std::unique_ptr<v8::TracingController> tracing_controller) { 63 if (in_process_stack_dumping == InProcessStackDumping::kEnabled) { 64 v8::base::debug::EnableInProcessStackDumping(); 65 } 66 auto platform = std::make_unique<DefaultPlatform>( 67 0, idle_task_support, std::move(tracing_controller)); 68 return platform; 69} 70 71V8_PLATFORM_EXPORT std::unique_ptr<JobHandle> NewDefaultJobHandle( 72 Platform* platform, TaskPriority priority, 73 std::unique_ptr<JobTask> job_task, size_t num_worker_threads) { 74 return std::make_unique<DefaultJobHandle>(std::make_shared<DefaultJobState>( 75 platform, std::move(job_task), priority, num_worker_threads)); 76} 77 78bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate, 79 MessageLoopBehavior behavior) { 80 return static_cast<DefaultPlatform*>(platform)->PumpMessageLoop(isolate, 81 behavior); 82} 83 84void RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate, 85 double idle_time_in_seconds) { 86 static_cast<DefaultPlatform*>(platform)->RunIdleTasks(isolate, 87 idle_time_in_seconds); 88} 89 90void NotifyIsolateShutdown(v8::Platform* platform, Isolate* isolate) { 91 static_cast<DefaultPlatform*>(platform)->NotifyIsolateShutdown(isolate); 92} 93 94DefaultPlatform::DefaultPlatform( 95 int thread_pool_size, IdleTaskSupport idle_task_support, 96 std::unique_ptr<v8::TracingController> tracing_controller) 97 : thread_pool_size_(thread_pool_size), 98 idle_task_support_(idle_task_support), 99 tracing_controller_(std::move(tracing_controller)), 100 page_allocator_(std::make_unique<v8::base::PageAllocator>()) { 101 if (!tracing_controller_) { 102 tracing::TracingController* controller = new tracing::TracingController(); 103#if !defined(V8_USE_PERFETTO) 104 controller->Initialize(nullptr); 105#endif 106 tracing_controller_.reset(controller); 107 } 108 if (thread_pool_size_ > 0) { 109 EnsureBackgroundTaskRunnerInitialized(); 110 } 111} 112 113DefaultPlatform::~DefaultPlatform() { 114 base::MutexGuard guard(&lock_); 115 if (worker_threads_task_runner_) worker_threads_task_runner_->Terminate(); 116 for (const auto& it : foreground_task_runner_map_) { 117 it.second->Terminate(); 118 } 119} 120 121namespace { 122 123double DefaultTimeFunction() { 124 return base::TimeTicks::Now().ToInternalValue() / 125 static_cast<double>(base::Time::kMicrosecondsPerSecond); 126} 127 128} // namespace 129 130void DefaultPlatform::EnsureBackgroundTaskRunnerInitialized() { 131 DCHECK_NULL(worker_threads_task_runner_); 132 worker_threads_task_runner_ = 133 std::make_shared<DefaultWorkerThreadsTaskRunner>( 134 thread_pool_size_, time_function_for_testing_ 135 ? time_function_for_testing_ 136 : DefaultTimeFunction); 137 DCHECK_NOT_NULL(worker_threads_task_runner_); 138} 139 140void DefaultPlatform::SetTimeFunctionForTesting( 141 DefaultPlatform::TimeFunction time_function) { 142 base::MutexGuard guard(&lock_); 143 time_function_for_testing_ = time_function; 144 // The time function has to be right after the construction of the platform. 145 DCHECK(foreground_task_runner_map_.empty()); 146} 147 148bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate, 149 MessageLoopBehavior wait_for_work) { 150 bool failed_result = wait_for_work == MessageLoopBehavior::kWaitForWork; 151 std::shared_ptr<DefaultForegroundTaskRunner> task_runner; 152 { 153 base::MutexGuard guard(&lock_); 154 auto it = foreground_task_runner_map_.find(isolate); 155 if (it == foreground_task_runner_map_.end()) return failed_result; 156 task_runner = it->second; 157 } 158 159 std::unique_ptr<Task> task = task_runner->PopTaskFromQueue(wait_for_work); 160 if (!task) return failed_result; 161 162 DefaultForegroundTaskRunner::RunTaskScope scope(task_runner); 163 task->Run(); 164 return true; 165} 166 167void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate, 168 double idle_time_in_seconds) { 169 DCHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_); 170 std::shared_ptr<DefaultForegroundTaskRunner> task_runner; 171 { 172 base::MutexGuard guard(&lock_); 173 if (foreground_task_runner_map_.find(isolate) == 174 foreground_task_runner_map_.end()) { 175 return; 176 } 177 task_runner = foreground_task_runner_map_[isolate]; 178 } 179 double deadline_in_seconds = 180 MonotonicallyIncreasingTime() + idle_time_in_seconds; 181 182 while (deadline_in_seconds > MonotonicallyIncreasingTime()) { 183 std::unique_ptr<IdleTask> task = task_runner->PopTaskFromIdleQueue(); 184 if (!task) return; 185 DefaultForegroundTaskRunner::RunTaskScope scope(task_runner); 186 task->Run(deadline_in_seconds); 187 } 188} 189 190std::shared_ptr<TaskRunner> DefaultPlatform::GetForegroundTaskRunner( 191 v8::Isolate* isolate) { 192 base::MutexGuard guard(&lock_); 193 if (foreground_task_runner_map_.find(isolate) == 194 foreground_task_runner_map_.end()) { 195 foreground_task_runner_map_.insert(std::make_pair( 196 isolate, std::make_shared<DefaultForegroundTaskRunner>( 197 idle_task_support_, time_function_for_testing_ 198 ? time_function_for_testing_ 199 : DefaultTimeFunction))); 200 } 201 return foreground_task_runner_map_[isolate]; 202} 203 204void DefaultPlatform::CallOnWorkerThread(std::unique_ptr<Task> task) { 205 // If this DCHECK fires, then this means that either 206 // - V8 is running without the --single-threaded flag but 207 // but the platform was created as a single-threaded platform. 208 // - or some component in V8 is ignoring --single-threaded 209 // and posting a background task. 210 DCHECK_NOT_NULL(worker_threads_task_runner_); 211 worker_threads_task_runner_->PostTask(std::move(task)); 212} 213 214void DefaultPlatform::CallDelayedOnWorkerThread(std::unique_ptr<Task> task, 215 double delay_in_seconds) { 216 // If this DCHECK fires, then this means that either 217 // - V8 is running without the --single-threaded flag but 218 // but the platform was created as a single-threaded platform. 219 // - or some component in V8 is ignoring --single-threaded 220 // and posting a background task. 221 DCHECK_NOT_NULL(worker_threads_task_runner_); 222 worker_threads_task_runner_->PostDelayedTask(std::move(task), 223 delay_in_seconds); 224} 225 226bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) { 227 return idle_task_support_ == IdleTaskSupport::kEnabled; 228} 229 230std::unique_ptr<JobHandle> DefaultPlatform::PostJob( 231 TaskPriority priority, std::unique_ptr<JobTask> job_task) { 232 size_t num_worker_threads = NumberOfWorkerThreads(); 233 if (priority == TaskPriority::kBestEffort && num_worker_threads > 2) { 234 num_worker_threads = 2; 235 } 236 return NewDefaultJobHandle(this, priority, std::move(job_task), 237 num_worker_threads); 238} 239 240double DefaultPlatform::MonotonicallyIncreasingTime() { 241 if (time_function_for_testing_) return time_function_for_testing_(); 242 return DefaultTimeFunction(); 243} 244 245double DefaultPlatform::CurrentClockTimeMillis() { 246 return base::OS::TimeCurrentMillis(); 247} 248 249TracingController* DefaultPlatform::GetTracingController() { 250 return tracing_controller_.get(); 251} 252 253void DefaultPlatform::SetTracingController( 254 std::unique_ptr<v8::TracingController> tracing_controller) { 255 DCHECK_NOT_NULL(tracing_controller.get()); 256 tracing_controller_ = std::move(tracing_controller); 257} 258 259int DefaultPlatform::NumberOfWorkerThreads() { return thread_pool_size_; } 260 261Platform::StackTracePrinter DefaultPlatform::GetStackTracePrinter() { 262 return PrintStackTrace; 263} 264 265v8::PageAllocator* DefaultPlatform::GetPageAllocator() { 266 return page_allocator_.get(); 267} 268 269void DefaultPlatform::NotifyIsolateShutdown(Isolate* isolate) { 270 base::MutexGuard guard(&lock_); 271 auto it = foreground_task_runner_map_.find(isolate); 272 if (it != foreground_task_runner_map_.end()) { 273 it->second->Terminate(); 274 foreground_task_runner_map_.erase(it); 275 } 276} 277 278} // namespace platform 279} // namespace v8 280