v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
d8-platforms.cc
Go to the documentation of this file.
1// Copyright 2018 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
6
7#include <memory>
8#include <unordered_map>
9
11#include "include/v8-platform.h"
12#include "src/base/logging.h"
13#include "src/base/macros.h"
18
19namespace v8 {
20
21class PredictablePlatform final : public Platform {
22 public:
23 explicit PredictablePlatform(std::unique_ptr<Platform> platform)
24 : platform_(std::move(platform)) {
26 }
27
30
32 return platform_->GetPageAllocator();
33 }
34
35 void OnCriticalMemoryPressure() override {
36 platform_->OnCriticalMemoryPressure();
37 }
38
39 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
40 v8::Isolate* isolate, TaskPriority priority) override {
41 return platform_->GetForegroundTaskRunner(isolate, priority);
42 }
43
44 int NumberOfWorkerThreads() override {
45 // The predictable platform executes everything on the main thread, but we
46 // still pretend to have the default number of worker threads to not
47 // unnecessarily change behaviour of the platform.
48 return platform_->NumberOfWorkerThreads();
49 }
50
52 std::unique_ptr<Task> task,
53 const SourceLocation& location) override {
54 // We post worker tasks on the foreground task runner of the
55 // {kProcessGlobalPredictablePlatformWorkerTaskQueue} isolate. The task
56 // queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue} isolate
57 // is then executed on the main thread to achieve predictable behavior.
58 //
59 // In this context here it is okay to call {GetForegroundTaskRunner} from a
60 // background thread. The reason is that code is executed sequentially with
61 // the PredictablePlatform, and that the {DefaultPlatform} does not access
62 // the isolate but only uses it as the key in a HashMap.
64 ->GetForegroundTaskRunner(
66 ->PostTask(std::move(task));
67 }
68
70 TaskPriority priority, std::unique_ptr<Task> task,
71 double delay_in_seconds, const SourceLocation& location) override {
72 // Never run delayed tasks.
73 }
74
75 bool IdleTasksEnabled(Isolate* isolate) override { return false; }
76
77 std::unique_ptr<JobHandle> CreateJobImpl(
78 TaskPriority priority, std::unique_ptr<JobTask> job_task,
79 const SourceLocation& location) override {
80 // Do not call {platform_->PostJob} here, as this would create a job that
81 // posts tasks directly to the underlying default platform.
82 return platform::NewDefaultJobHandle(this, priority, std::move(job_task),
84 }
85
86 double MonotonicallyIncreasingTime() override {
87 // In predictable mode, there should be no (observable) concurrency, but we
88 // still run some tests that explicitly specify '--predictable' in the
89 // '--isolates' variant, where several threads run the same test in
90 // different isolates. To avoid TSan issues in that scenario we use atomic
91 // increments here.
92 uint64_t synthetic_time =
93 synthetic_time_.fetch_add(1, std::memory_order_relaxed);
94 return 1e-5 * synthetic_time;
95 }
96
100
102 return platform_->GetTracingController();
103 }
104
105 Platform* platform() const { return platform_.get(); }
106
107 private:
108 std::atomic<uint64_t> synthetic_time_{0};
109 std::unique_ptr<Platform> platform_;
110};
111
112std::unique_ptr<Platform> MakePredictablePlatform(
113 std::unique_ptr<Platform> platform) {
114 return std::make_unique<PredictablePlatform>(std::move(platform));
115}
116
117class DelayedTasksPlatform final : public Platform {
118 public:
119 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform)
120 : platform_(std::move(platform)) {
122 }
123
124 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform,
125 int64_t random_seed)
126 : platform_(std::move(platform)), rng_(random_seed) {
128 }
129
132
134 // When the platform shuts down, all task runners must be freed.
136 }
137
139 return platform_->GetPageAllocator();
140 }
141
142 void OnCriticalMemoryPressure() override {
143 platform_->OnCriticalMemoryPressure();
144 }
145
146 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
147 v8::Isolate* isolate, TaskPriority priority) override {
148 std::shared_ptr<TaskRunner> runner =
149 platform_->GetForegroundTaskRunner(isolate, priority);
150
151 base::MutexGuard lock_guard(&mutex_);
152 // Check if we can re-materialize the weak ptr in our map.
153 std::weak_ptr<DelayedTaskRunner>& weak_delayed_runner =
154 delayed_task_runners_[runner.get()];
155 std::shared_ptr<DelayedTaskRunner> delayed_runner =
156 weak_delayed_runner.lock();
157
158 if (!delayed_runner) {
159 // Create a new {DelayedTaskRunner} and keep a weak reference in our map.
160 delayed_runner = std::make_shared<DelayedTaskRunner>(runner, this);
161 weak_delayed_runner = delayed_runner;
162 }
163
164 return std::move(delayed_runner);
165 }
166
167 int NumberOfWorkerThreads() override {
168 return platform_->NumberOfWorkerThreads();
169 }
170
172 std::unique_ptr<Task> task,
173 const SourceLocation& location) override {
174 platform_->PostTaskOnWorkerThread(
175 priority, MakeDelayedTask(std::move(task)), location);
176 }
177
179 TaskPriority priority, std::unique_ptr<Task> task,
180 double delay_in_seconds, const SourceLocation& location) override {
181 platform_->PostDelayedTaskOnWorkerThread(
182 priority, MakeDelayedTask(std::move(task)), delay_in_seconds, location);
183 }
184
185 bool IdleTasksEnabled(Isolate* isolate) override {
186 return platform_->IdleTasksEnabled(isolate);
187 }
188
189 std::unique_ptr<JobHandle> CreateJobImpl(
190 TaskPriority priority, std::unique_ptr<JobTask> job_task,
191 const SourceLocation& location) override {
192 return platform_->CreateJob(priority, MakeDelayedJob(std::move(job_task)),
193 location);
194 }
195
196 double MonotonicallyIncreasingTime() override {
197 return platform_->MonotonicallyIncreasingTime();
198 }
199
200 double CurrentClockTimeMillis() override {
201 return platform_->CurrentClockTimeMillis();
202 }
203
205 return platform_->GetTracingController();
206 }
207
208 private:
209 class DelayedTaskRunner final : public TaskRunner {
210 public:
211 DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner,
212 DelayedTasksPlatform* platform)
213 : task_runner_(task_runner), platform_(platform) {}
214
216 TaskRunner* original_runner = task_runner_.get();
217 base::MutexGuard lock_guard(&platform_->mutex_);
218 auto& delayed_task_runners = platform_->delayed_task_runners_;
219 DCHECK_EQ(1, delayed_task_runners.count(original_runner));
220 delayed_task_runners.erase(original_runner);
221 }
222
223 bool IdleTasksEnabled() final { return task_runner_->IdleTasksEnabled(); }
224
225 bool NonNestableTasksEnabled() const final {
226 return task_runner_->NonNestableTasksEnabled();
227 }
228
229 private:
230 void PostTaskImpl(std::unique_ptr<Task> task,
231 const SourceLocation& location) final {
232 task_runner_->PostTask(platform_->MakeDelayedTask(std::move(task)),
233 location);
234 }
235
236 void PostNonNestableTaskImpl(std::unique_ptr<Task> task,
237 const SourceLocation& location) final {
238 task_runner_->PostNonNestableTask(
239 platform_->MakeDelayedTask(std::move(task)), location);
240 }
241
242 void PostDelayedTaskImpl(std::unique_ptr<Task> task,
243 double delay_in_seconds,
244 const SourceLocation& location) final {
245 task_runner_->PostDelayedTask(platform_->MakeDelayedTask(std::move(task)),
246 delay_in_seconds, location);
247 }
248
249 void PostIdleTaskImpl(std::unique_ptr<IdleTask> task,
250 const SourceLocation& location) final {
251 task_runner_->PostIdleTask(
252 platform_->MakeDelayedIdleTask(std::move(task)), location);
253 }
254
255 private:
256 std::shared_ptr<TaskRunner> task_runner_;
258 };
259
260 class DelayedTask final : public Task {
261 public:
262 DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms)
263 : task_(std::move(task)), delay_ms_(delay_ms) {}
264
265 void Run() override {
267 task_->Run();
268 }
269
270 private:
271 std::unique_ptr<Task> task_;
272 int32_t delay_ms_;
273 };
274
275 class DelayedIdleTask final : public IdleTask {
276 public:
277 DelayedIdleTask(std::unique_ptr<IdleTask> task, int32_t delay_ms)
278 : task_(std::move(task)), delay_ms_(delay_ms) {}
279
280 void Run(double deadline_in_seconds) override {
282 task_->Run(deadline_in_seconds);
283 }
284
285 private:
286 std::unique_ptr<IdleTask> task_;
287 int32_t delay_ms_;
288 };
289
290 class DelayedJob final : public JobTask {
291 public:
292 DelayedJob(std::unique_ptr<JobTask> job_task, int32_t delay_ms)
293 : job_task_(std::move(job_task)), delay_ms_(delay_ms) {}
294
295 void Run(JobDelegate* delegate) override {
296 // If this job is being executed via worker tasks (as e.g. the
297 // {DefaultJobHandle} implementation does it), the worker task would
298 // already include a delay. In order to not depend on that, we add our own
299 // delay here anyway.
301 job_task_->Run(delegate);
302 }
303
304 size_t GetMaxConcurrency(size_t worker_count) const override {
305 return job_task_->GetMaxConcurrency(worker_count);
306 }
307
308 private:
309 std::unique_ptr<JobTask> job_task_;
310 int32_t delay_ms_;
311 };
312
313 std::unique_ptr<Platform> platform_;
314
315 // The Mutex protects the RNG, which is used by foreground and background
316 // threads, and the {delayed_task_runners_} map might be accessed concurrently
317 // by the shared_ptr destructor.
320 std::unordered_map<TaskRunner*, std::weak_ptr<DelayedTaskRunner>>
322
324 base::MutexGuard lock_guard(&mutex_);
325 double delay_fraction = rng_.NextDouble();
326 // Sleep up to 100ms (100000us). Square {delay_fraction} to shift
327 // distribution towards shorter sleeps.
328 return 1e5 * (delay_fraction * delay_fraction);
329 }
330
331 std::unique_ptr<Task> MakeDelayedTask(std::unique_ptr<Task> task) {
332 return std::make_unique<DelayedTask>(std::move(task),
334 }
335
336 std::unique_ptr<IdleTask> MakeDelayedIdleTask(
337 std::unique_ptr<IdleTask> task) {
338 return std::make_unique<DelayedIdleTask>(std::move(task),
340 }
341
342 std::unique_ptr<JobTask> MakeDelayedJob(std::unique_ptr<JobTask> task) {
343 return std::make_unique<DelayedJob>(std::move(task),
345 }
346};
347
348std::unique_ptr<Platform> MakeDelayedTasksPlatform(
349 std::unique_ptr<Platform> platform, int64_t random_seed) {
350 if (random_seed) {
351 return std::make_unique<DelayedTasksPlatform>(std::move(platform),
352 random_seed);
353 }
354 return std::make_unique<DelayedTasksPlatform>(std::move(platform));
355}
356
357} // namespace v8
void Run(double deadline_in_seconds) override
DelayedIdleTask(std::unique_ptr< IdleTask > task, int32_t delay_ms)
std::unique_ptr< JobTask > job_task_
void Run(JobDelegate *delegate) override
DelayedJob(std::unique_ptr< JobTask > job_task, int32_t delay_ms)
size_t GetMaxConcurrency(size_t worker_count) const override
void PostNonNestableTaskImpl(std::unique_ptr< Task > task, const SourceLocation &location) final
std::shared_ptr< TaskRunner > task_runner_
void PostIdleTaskImpl(std::unique_ptr< IdleTask > task, const SourceLocation &location) final
void PostTaskImpl(std::unique_ptr< Task > task, const SourceLocation &location) final
void PostDelayedTaskImpl(std::unique_ptr< Task > task, double delay_in_seconds, const SourceLocation &location) final
DelayedTaskRunner(std::shared_ptr< TaskRunner > task_runner, DelayedTasksPlatform *platform)
DelayedTask(std::unique_ptr< Task > task, int32_t delay_ms)
double MonotonicallyIncreasingTime() override
std::unique_ptr< IdleTask > MakeDelayedIdleTask(std::unique_ptr< IdleTask > task)
double CurrentClockTimeMillis() override
std::unordered_map< TaskRunner *, std::weak_ptr< DelayedTaskRunner > > delayed_task_runners_
int NumberOfWorkerThreads() override
bool IdleTasksEnabled(Isolate *isolate) override
int32_t GetRandomDelayInMilliseconds()
std::unique_ptr< Platform > platform_
std::unique_ptr< Task > MakeDelayedTask(std::unique_ptr< Task > task)
std::unique_ptr< JobHandle > CreateJobImpl(TaskPriority priority, std::unique_ptr< JobTask > job_task, const SourceLocation &location) override
void OnCriticalMemoryPressure() override
std::shared_ptr< TaskRunner > GetForegroundTaskRunner(v8::Isolate *isolate, TaskPriority priority) override
base::RandomNumberGenerator rng_
DelayedTasksPlatform(std::unique_ptr< Platform > platform)
DelayedTasksPlatform(const DelayedTasksPlatform &)=delete
PageAllocator * GetPageAllocator() override
v8::TracingController * GetTracingController() override
DelayedTasksPlatform & operator=(const DelayedTasksPlatform &)=delete
std::unique_ptr< JobTask > MakeDelayedJob(std::unique_ptr< JobTask > task)
void PostTaskOnWorkerThreadImpl(TaskPriority priority, std::unique_ptr< Task > task, const SourceLocation &location) override
void PostDelayedTaskOnWorkerThreadImpl(TaskPriority priority, std::unique_ptr< Task > task, double delay_in_seconds, const SourceLocation &location) override
DelayedTasksPlatform(std::unique_ptr< Platform > platform, int64_t random_seed)
std::shared_ptr< TaskRunner > GetForegroundTaskRunner(v8::Isolate *isolate, TaskPriority priority) override
Platform * platform() const
double MonotonicallyIncreasingTime() override
PredictablePlatform & operator=(const PredictablePlatform &)=delete
std::unique_ptr< JobHandle > CreateJobImpl(TaskPriority priority, std::unique_ptr< JobTask > job_task, const SourceLocation &location) override
double CurrentClockTimeMillis() override
v8::TracingController * GetTracingController() override
PageAllocator * GetPageAllocator() override
void PostTaskOnWorkerThreadImpl(TaskPriority priority, std::unique_ptr< Task > task, const SourceLocation &location) override
int NumberOfWorkerThreads() override
void OnCriticalMemoryPressure() override
std::unique_ptr< Platform > platform_
bool IdleTasksEnabled(Isolate *isolate) override
void PostDelayedTaskOnWorkerThreadImpl(TaskPriority priority, std::unique_ptr< Task > task, double delay_in_seconds, const SourceLocation &location) override
PredictablePlatform(std::unique_ptr< Platform > platform)
PredictablePlatform(const PredictablePlatform &)=delete
std::atomic< uint64_t > synthetic_time_
static void Sleep(TimeDelta interval)
double NextDouble() V8_WARN_UNUSED_RESULT
static constexpr int64_t kMillisecondsPerSecond
Definition time.h:45
static constexpr TimeDelta FromMicroseconds(int64_t microseconds)
Definition time.h:87
size_t priority
STL namespace.
V8_PLATFORM_EXPORT std::unique_ptr< JobHandle > NewDefaultJobHandle(Platform *platform, TaskPriority priority, std::unique_ptr< JobTask > job_task, size_t num_worker_threads)
TaskPriority
Definition v8-platform.h:24
constexpr Isolate * kProcessGlobalPredictablePlatformWorkerTaskQueue
std::unique_ptr< Platform > MakePredictablePlatform(std::unique_ptr< Platform > platform)
std::unique_ptr< Platform > MakeDelayedTasksPlatform(std::unique_ptr< Platform > platform, int64_t random_seed)
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_EQ(v1, v2)
Definition logging.h:485