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
5#include <memory>
6#include <unordered_map>
7
8#include "include/v8-platform.h"
9#include "src/base/logging.h"
10#include "src/base/macros.h"
11#include "src/base/platform/mutex.h"
12#include "src/base/platform/platform.h"
13#include "src/base/platform/time.h"
14#include "src/base/template-utils.h"
15#include "src/base/utils/random-number-generator.h"
16#include "src/d8-platforms.h"
17
18namespace v8 {
19
20class PredictablePlatform : public Platform {
21 public:
22 explicit PredictablePlatform(std::unique_ptr<Platform> platform)
23 : platform_(std::move(platform)) {
24 DCHECK_NOT_NULL(platform_);
25 }
26
27 PageAllocator* GetPageAllocator() override {
28 return platform_->GetPageAllocator();
29 }
30
31 void OnCriticalMemoryPressure() override {
32 platform_->OnCriticalMemoryPressure();
33 }
34
35 bool OnCriticalMemoryPressure(size_t length) override {
36 return platform_->OnCriticalMemoryPressure(length);
37 }
38
39 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
40 v8::Isolate* isolate) override {
41 return platform_->GetForegroundTaskRunner(isolate);
42 }
43
44 int NumberOfWorkerThreads() override { return 0; }
45
46 void CallOnWorkerThread(std::unique_ptr<Task> task) override {
47 // It's not defined when background tasks are being executed, so we can just
48 // execute them right away.
49 task->Run();
50 }
51
52 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
53 double delay_in_seconds) override {
54 // Never run delayed tasks.
55 }
56
57 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
58 // This is a deprecated function and should not be called anymore.
59 UNREACHABLE();
60 }
61
62 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
63 double delay_in_seconds) override {
64 // This is a deprecated function and should not be called anymore.
65 UNREACHABLE();
66 }
67
68 void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
69 UNREACHABLE();
70 }
71
72 bool IdleTasksEnabled(Isolate* isolate) override { return false; }
73
74 double MonotonicallyIncreasingTime() override {
75 return synthetic_time_in_sec_ += 0.00001;
76 }
77
78 double CurrentClockTimeMillis() override {
79 return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
80 }
81
82 v8::TracingController* GetTracingController() override {
83 return platform_->GetTracingController();
84 }
85
86 Platform* platform() const { return platform_.get(); }
87
88 private:
89 double synthetic_time_in_sec_ = 0.0;
90 std::unique_ptr<Platform> platform_;
91
92 DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
93};
94
95std::unique_ptr<Platform> MakePredictablePlatform(
96 std::unique_ptr<Platform> platform) {
97 return base::make_unique<PredictablePlatform>(std::move(platform));
98}
99
100class DelayedTasksPlatform : public Platform {
101 public:
102 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform)
103 : platform_(std::move(platform)) {
104 DCHECK_NOT_NULL(platform_);
105 }
106
107 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform,
108 int64_t random_seed)
109 : platform_(std::move(platform)), rng_(random_seed) {
110 DCHECK_NOT_NULL(platform_);
111 }
112
113 ~DelayedTasksPlatform() {
114 // When the platform shuts down, all task runners must be freed.
115 DCHECK_EQ(0, delayed_task_runners_.size());
116 }
117
118 PageAllocator* GetPageAllocator() override {
119 return platform_->GetPageAllocator();
120 }
121
122 void OnCriticalMemoryPressure() override {
123 platform_->OnCriticalMemoryPressure();
124 }
125
126 bool OnCriticalMemoryPressure(size_t length) override {
127 return platform_->OnCriticalMemoryPressure(length);
128 }
129
130 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
131 v8::Isolate* isolate) override {
132 std::shared_ptr<TaskRunner> runner =
133 platform_->GetForegroundTaskRunner(isolate);
134
135 base::MutexGuard lock_guard(&mutex_);
136 // Check if we can re-materialize the weak ptr in our map.
137 std::weak_ptr<DelayedTaskRunner>& weak_delayed_runner =
138 delayed_task_runners_[runner.get()];
139 std::shared_ptr<DelayedTaskRunner> delayed_runner =
140 weak_delayed_runner.lock();
141
142 if (!delayed_runner) {
143 // Create a new {DelayedTaskRunner} and keep a weak reference in our map.
144 delayed_runner.reset(new DelayedTaskRunner(runner, this),
145 DelayedTaskRunnerDeleter{});
146 weak_delayed_runner = delayed_runner;
147 }
148
149 return std::move(delayed_runner);
150 }
151
152 int NumberOfWorkerThreads() override {
153 return platform_->NumberOfWorkerThreads();
154 }
155
156 void CallOnWorkerThread(std::unique_ptr<Task> task) override {
157 platform_->CallOnWorkerThread(MakeDelayedTask(std::move(task)));
158 }
159
160 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
161 double delay_in_seconds) override {
162 platform_->CallDelayedOnWorkerThread(MakeDelayedTask(std::move(task)),
163 delay_in_seconds);
164 }
165
166 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
167 // This is a deprecated function and should not be called anymore.
168 UNREACHABLE();
169 }
170
171 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
172 double delay_in_seconds) override {
173 // This is a deprecated function and should not be called anymore.
174 UNREACHABLE();
175 }
176
177 void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
178 // This is a deprecated function and should not be called anymore.
179 UNREACHABLE();
180 }
181
182 bool IdleTasksEnabled(Isolate* isolate) override {
183 return platform_->IdleTasksEnabled(isolate);
184 }
185
186 double MonotonicallyIncreasingTime() override {
187 return platform_->MonotonicallyIncreasingTime();
188 }
189
190 double CurrentClockTimeMillis() override {
191 return platform_->CurrentClockTimeMillis();
192 }
193
194 v8::TracingController* GetTracingController() override {
195 return platform_->GetTracingController();
196 }
197
198 private:
199 class DelayedTaskRunnerDeleter;
200 class DelayedTaskRunner final : public TaskRunner {
201 public:
202 DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner,
203 DelayedTasksPlatform* platform)
204 : task_runner_(task_runner), platform_(platform) {}
205
206 void PostTask(std::unique_ptr<Task> task) final {
207 task_runner_->PostTask(platform_->MakeDelayedTask(std::move(task)));
208 }
209
210 void PostDelayedTask(std::unique_ptr<Task> task,
211 double delay_in_seconds) final {
212 task_runner_->PostDelayedTask(platform_->MakeDelayedTask(std::move(task)),
213 delay_in_seconds);
214 }
215
216 void PostIdleTask(std::unique_ptr<IdleTask> task) final {
217 task_runner_->PostIdleTask(
218 platform_->MakeDelayedIdleTask(std::move(task)));
219 }
220
221 bool IdleTasksEnabled() final { return task_runner_->IdleTasksEnabled(); }
222
223 private:
224 friend class DelayedTaskRunnerDeleter;
225 std::shared_ptr<TaskRunner> task_runner_;
226 DelayedTasksPlatform* platform_;
227 };
228
229 class DelayedTaskRunnerDeleter {
230 public:
231 void operator()(DelayedTaskRunner* runner) const {
232 TaskRunner* original_runner = runner->task_runner_.get();
233 base::MutexGuard lock_guard(&runner->platform_->mutex_);
234 auto& delayed_task_runners = runner->platform_->delayed_task_runners_;
235 DCHECK_EQ(1, delayed_task_runners.count(original_runner));
236 delayed_task_runners.erase(original_runner);
237 }
238 };
239
240 class DelayedTask : public Task {
241 public:
242 DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms)
243 : task_(std::move(task)), delay_ms_(delay_ms) {}
244 void Run() final {
245 base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
246 task_->Run();
247 }
248
249 private:
250 std::unique_ptr<Task> task_;
251 int32_t delay_ms_;
252 };
253
254 class DelayedIdleTask : public IdleTask {
255 public:
256 DelayedIdleTask(std::unique_ptr<IdleTask> task, int32_t delay_ms)
257 : task_(std::move(task)), delay_ms_(delay_ms) {}
258 void Run(double deadline_in_seconds) final {
259 base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
260 task_->Run(deadline_in_seconds);
261 }
262
263 private:
264 std::unique_ptr<IdleTask> task_;
265 int32_t delay_ms_;
266 };
267
268 std::unique_ptr<Platform> platform_;
269
270 // The Mutex protects the RNG, which is used by foreground and background
271 // threads, and the {delayed_task_runners_} map might be accessed concurrently
272 // by the shared_ptr destructor.
273 base::Mutex mutex_;
274 base::RandomNumberGenerator rng_;
275 std::unordered_map<TaskRunner*, std::weak_ptr<DelayedTaskRunner>>
276 delayed_task_runners_;
277
278 int32_t GetRandomDelayInMilliseconds() {
279 base::MutexGuard lock_guard(&mutex_);
280 double delay_fraction = rng_.NextDouble();
281 // Sleep up to 100ms (100000us). Square {delay_fraction} to shift
282 // distribution towards shorter sleeps.
283 return 1e5 * (delay_fraction * delay_fraction);
284 }
285
286 std::unique_ptr<Task> MakeDelayedTask(std::unique_ptr<Task> task) {
287 return base::make_unique<DelayedTask>(std::move(task),
288 GetRandomDelayInMilliseconds());
289 }
290
291 std::unique_ptr<IdleTask> MakeDelayedIdleTask(
292 std::unique_ptr<IdleTask> task) {
293 return base::make_unique<DelayedIdleTask>(std::move(task),
294 GetRandomDelayInMilliseconds());
295 }
296
297 DISALLOW_COPY_AND_ASSIGN(DelayedTasksPlatform);
298};
299
300std::unique_ptr<Platform> MakeDelayedTasksPlatform(
301 std::unique_ptr<Platform> platform, int64_t random_seed) {
302 if (random_seed) {
303 return base::make_unique<DelayedTasksPlatform>(std::move(platform),
304 random_seed);
305 }
306 return base::make_unique<DelayedTasksPlatform>(std::move(platform));
307}
308
309} // namespace v8
310