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
NotifyIsolateShutdown(v8::Platform * platform,Isolate * isolate)90 void NotifyIsolateShutdown(v8::Platform* platform, Isolate* isolate) {
91 static_cast<DefaultPlatform*>(platform)->NotifyIsolateShutdown(isolate);
92 }
93
DefaultPlatform(int thread_pool_size,IdleTaskSupport idle_task_support,std::unique_ptr<v8::TracingController> tracing_controller)94 DefaultPlatform::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
~DefaultPlatform()113 DefaultPlatform::~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
121 namespace {
122
DefaultTimeFunction()123 double DefaultTimeFunction() {
124 return base::TimeTicks::Now().ToInternalValue() /
125 static_cast<double>(base::Time::kMicrosecondsPerSecond);
126 }
127
128 } // namespace
129
EnsureBackgroundTaskRunnerInitialized()130 void 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
SetTimeFunctionForTesting(DefaultPlatform::TimeFunction time_function)140 void 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
PumpMessageLoop(v8::Isolate * isolate,MessageLoopBehavior wait_for_work)148 bool 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
RunIdleTasks(v8::Isolate * isolate,double idle_time_in_seconds)167 void 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
GetForegroundTaskRunner(v8::Isolate * isolate)190 std::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
CallOnWorkerThread(std::unique_ptr<Task> task)204 void 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
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)214 void 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
IdleTasksEnabled(Isolate * isolate)226 bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) {
227 return idle_task_support_ == IdleTaskSupport::kEnabled;
228 }
229
PostJob(TaskPriority priority,std::unique_ptr<JobTask> job_task)230 std::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
MonotonicallyIncreasingTime()240 double DefaultPlatform::MonotonicallyIncreasingTime() {
241 if (time_function_for_testing_) return time_function_for_testing_();
242 return DefaultTimeFunction();
243 }
244
CurrentClockTimeMillis()245 double DefaultPlatform::CurrentClockTimeMillis() {
246 return base::OS::TimeCurrentMillis();
247 }
248
GetTracingController()249 TracingController* DefaultPlatform::GetTracingController() {
250 return tracing_controller_.get();
251 }
252
SetTracingController(std::unique_ptr<v8::TracingController> tracing_controller)253 void 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
NumberOfWorkerThreads()259 int DefaultPlatform::NumberOfWorkerThreads() { return thread_pool_size_; }
260
GetStackTracePrinter()261 Platform::StackTracePrinter DefaultPlatform::GetStackTracePrinter() {
262 return PrintStackTrace;
263 }
264
GetPageAllocator()265 v8::PageAllocator* DefaultPlatform::GetPageAllocator() {
266 return page_allocator_.get();
267 }
268
NotifyIsolateShutdown(Isolate * isolate)269 void 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