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