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