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