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
22 namespace v8 {
23 namespace platform {
24
25 namespace {
26
PrintStackTrace()27 void 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
34 constexpr int kMaxThreadPoolSize = 16;
35
GetActualThreadPoolSize(int thread_pool_size)36 int 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
NewDefaultPlatform(int thread_pool_size,IdleTaskSupport idle_task_support,InProcessStackDumping in_process_stack_dumping,std::unique_ptr<v8::TracingController> tracing_controller)46 std::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
NewSingleThreadedDefaultPlatform(IdleTaskSupport idle_task_support,InProcessStackDumping in_process_stack_dumping,std::unique_ptr<v8::TracingController> tracing_controller)59 std::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
NewDefaultJobHandle(Platform * platform,TaskPriority priority,std::unique_ptr<JobTask> job_task,size_t num_worker_threads)71 V8_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
PumpMessageLoop(v8::Platform * platform,v8::Isolate * isolate,MessageLoopBehavior behavior)78 bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate,
79 MessageLoopBehavior behavior) {
80 return static_cast<DefaultPlatform*>(platform)->PumpMessageLoop(isolate,
81 behavior);
82 }
83
RunIdleTasks(v8::Platform * platform,v8::Isolate * isolate,double idle_time_in_seconds)84 void 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
SetTracingController(v8::Platform * platform,v8::platform::tracing::TracingController * tracing_controller)90 void SetTracingController(
91 v8::Platform* platform,
92 v8::platform::tracing::TracingController* tracing_controller) {
93 static_cast<DefaultPlatform*>(platform)->SetTracingController(
94 std::unique_ptr<v8::TracingController>(tracing_controller));
95 }
96
NotifyIsolateShutdown(v8::Platform * platform,Isolate * isolate)97 void NotifyIsolateShutdown(v8::Platform* platform, Isolate* isolate) {
98 static_cast<DefaultPlatform*>(platform)->NotifyIsolateShutdown(isolate);
99 }
100
DefaultPlatform(int thread_pool_size,IdleTaskSupport idle_task_support,std::unique_ptr<v8::TracingController> tracing_controller)101 DefaultPlatform::DefaultPlatform(
102 int thread_pool_size, IdleTaskSupport idle_task_support,
103 std::unique_ptr<v8::TracingController> tracing_controller)
104 : thread_pool_size_(thread_pool_size),
105 idle_task_support_(idle_task_support),
106 tracing_controller_(std::move(tracing_controller)),
107 page_allocator_(std::make_unique<v8::base::PageAllocator>()) {
108 if (!tracing_controller_) {
109 tracing::TracingController* controller = new tracing::TracingController();
110 #if !defined(V8_USE_PERFETTO)
111 controller->Initialize(nullptr);
112 #endif
113 tracing_controller_.reset(controller);
114 }
115 if (thread_pool_size_ > 0) {
116 EnsureBackgroundTaskRunnerInitialized();
117 }
118 }
119
~DefaultPlatform()120 DefaultPlatform::~DefaultPlatform() {
121 base::MutexGuard guard(&lock_);
122 if (worker_threads_task_runner_) worker_threads_task_runner_->Terminate();
123 for (const auto& it : foreground_task_runner_map_) {
124 it.second->Terminate();
125 }
126 }
127
128 namespace {
129
DefaultTimeFunction()130 double DefaultTimeFunction() {
131 return base::TimeTicks::HighResolutionNow().ToInternalValue() /
132 static_cast<double>(base::Time::kMicrosecondsPerSecond);
133 }
134
135 } // namespace
136
EnsureBackgroundTaskRunnerInitialized()137 void DefaultPlatform::EnsureBackgroundTaskRunnerInitialized() {
138 DCHECK_NULL(worker_threads_task_runner_);
139 worker_threads_task_runner_ =
140 std::make_shared<DefaultWorkerThreadsTaskRunner>(
141 thread_pool_size_, time_function_for_testing_
142 ? time_function_for_testing_
143 : DefaultTimeFunction);
144 DCHECK_NOT_NULL(worker_threads_task_runner_);
145 }
146
SetTimeFunctionForTesting(DefaultPlatform::TimeFunction time_function)147 void DefaultPlatform::SetTimeFunctionForTesting(
148 DefaultPlatform::TimeFunction time_function) {
149 base::MutexGuard guard(&lock_);
150 time_function_for_testing_ = time_function;
151 // The time function has to be right after the construction of the platform.
152 DCHECK(foreground_task_runner_map_.empty());
153 }
154
PumpMessageLoop(v8::Isolate * isolate,MessageLoopBehavior wait_for_work)155 bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate,
156 MessageLoopBehavior wait_for_work) {
157 bool failed_result = wait_for_work == MessageLoopBehavior::kWaitForWork;
158 std::shared_ptr<DefaultForegroundTaskRunner> task_runner;
159 {
160 base::MutexGuard guard(&lock_);
161 auto it = foreground_task_runner_map_.find(isolate);
162 if (it == foreground_task_runner_map_.end()) return failed_result;
163 task_runner = it->second;
164 }
165
166 std::unique_ptr<Task> task = task_runner->PopTaskFromQueue(wait_for_work);
167 if (!task) return failed_result;
168
169 DefaultForegroundTaskRunner::RunTaskScope scope(task_runner);
170 task->Run();
171 return true;
172 }
173
RunIdleTasks(v8::Isolate * isolate,double idle_time_in_seconds)174 void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate,
175 double idle_time_in_seconds) {
176 DCHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_);
177 std::shared_ptr<DefaultForegroundTaskRunner> task_runner;
178 {
179 base::MutexGuard guard(&lock_);
180 if (foreground_task_runner_map_.find(isolate) ==
181 foreground_task_runner_map_.end()) {
182 return;
183 }
184 task_runner = foreground_task_runner_map_[isolate];
185 }
186 double deadline_in_seconds =
187 MonotonicallyIncreasingTime() + idle_time_in_seconds;
188
189 while (deadline_in_seconds > MonotonicallyIncreasingTime()) {
190 std::unique_ptr<IdleTask> task = task_runner->PopTaskFromIdleQueue();
191 if (!task) return;
192 DefaultForegroundTaskRunner::RunTaskScope scope(task_runner);
193 task->Run(deadline_in_seconds);
194 }
195 }
196
GetForegroundTaskRunner(v8::Isolate * isolate)197 std::shared_ptr<TaskRunner> DefaultPlatform::GetForegroundTaskRunner(
198 v8::Isolate* isolate) {
199 base::MutexGuard guard(&lock_);
200 if (foreground_task_runner_map_.find(isolate) ==
201 foreground_task_runner_map_.end()) {
202 foreground_task_runner_map_.insert(std::make_pair(
203 isolate, std::make_shared<DefaultForegroundTaskRunner>(
204 idle_task_support_, time_function_for_testing_
205 ? time_function_for_testing_
206 : DefaultTimeFunction)));
207 }
208 return foreground_task_runner_map_[isolate];
209 }
210
CallOnWorkerThread(std::unique_ptr<Task> task)211 void DefaultPlatform::CallOnWorkerThread(std::unique_ptr<Task> task) {
212 // If this DCHECK fires, then this means that either
213 // - V8 is running without the --single-threaded flag but
214 // but the platform was created as a single-threaded platform.
215 // - or some component in V8 is ignoring --single-threaded
216 // and posting a background task.
217 DCHECK_NOT_NULL(worker_threads_task_runner_);
218 worker_threads_task_runner_->PostTask(std::move(task));
219 }
220
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)221 void DefaultPlatform::CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
222 double delay_in_seconds) {
223 // If this DCHECK fires, then this means that either
224 // - V8 is running without the --single-threaded flag but
225 // but the platform was created as a single-threaded platform.
226 // - or some component in V8 is ignoring --single-threaded
227 // and posting a background task.
228 DCHECK_NOT_NULL(worker_threads_task_runner_);
229 worker_threads_task_runner_->PostDelayedTask(std::move(task),
230 delay_in_seconds);
231 }
232
IdleTasksEnabled(Isolate * isolate)233 bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) {
234 return idle_task_support_ == IdleTaskSupport::kEnabled;
235 }
236
PostJob(TaskPriority priority,std::unique_ptr<JobTask> job_task)237 std::unique_ptr<JobHandle> DefaultPlatform::PostJob(
238 TaskPriority priority, std::unique_ptr<JobTask> job_task) {
239 size_t num_worker_threads = NumberOfWorkerThreads();
240 if (priority == TaskPriority::kBestEffort && num_worker_threads > 2) {
241 num_worker_threads = 2;
242 }
243 return NewDefaultJobHandle(this, priority, std::move(job_task),
244 num_worker_threads);
245 }
246
MonotonicallyIncreasingTime()247 double DefaultPlatform::MonotonicallyIncreasingTime() {
248 if (time_function_for_testing_) return time_function_for_testing_();
249 return DefaultTimeFunction();
250 }
251
CurrentClockTimeMillis()252 double DefaultPlatform::CurrentClockTimeMillis() {
253 return base::OS::TimeCurrentMillis();
254 }
255
GetTracingController()256 TracingController* DefaultPlatform::GetTracingController() {
257 return tracing_controller_.get();
258 }
259
SetTracingController(std::unique_ptr<v8::TracingController> tracing_controller)260 void DefaultPlatform::SetTracingController(
261 std::unique_ptr<v8::TracingController> tracing_controller) {
262 DCHECK_NOT_NULL(tracing_controller.get());
263 tracing_controller_ = std::move(tracing_controller);
264 }
265
NumberOfWorkerThreads()266 int DefaultPlatform::NumberOfWorkerThreads() { return thread_pool_size_; }
267
GetStackTracePrinter()268 Platform::StackTracePrinter DefaultPlatform::GetStackTracePrinter() {
269 return PrintStackTrace;
270 }
271
GetPageAllocator()272 v8::PageAllocator* DefaultPlatform::GetPageAllocator() {
273 return page_allocator_.get();
274 }
275
NotifyIsolateShutdown(Isolate * isolate)276 void DefaultPlatform::NotifyIsolateShutdown(Isolate* isolate) {
277 base::MutexGuard guard(&lock_);
278 auto it = foreground_task_runner_map_.find(isolate);
279 if (it != foreground_task_runner_map_.end()) {
280 it->second->Terminate();
281 foreground_task_runner_map_.erase(it);
282 }
283 }
284
285 } // namespace platform
286 } // namespace v8
287