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/utils/random-number-generator.h"
15 #include "src/d8/d8-platforms.h"
16
17 namespace v8 {
18
19 class PredictablePlatform final : public Platform {
20 public:
PredictablePlatform(std::unique_ptr<Platform> platform)21 explicit PredictablePlatform(std::unique_ptr<Platform> platform)
22 : platform_(std::move(platform)) {
23 DCHECK_NOT_NULL(platform_);
24 }
25
GetPageAllocator()26 PageAllocator* GetPageAllocator() override {
27 return platform_->GetPageAllocator();
28 }
29
OnCriticalMemoryPressure()30 void OnCriticalMemoryPressure() override {
31 platform_->OnCriticalMemoryPressure();
32 }
33
OnCriticalMemoryPressure(size_t length)34 bool OnCriticalMemoryPressure(size_t length) override {
35 return platform_->OnCriticalMemoryPressure(length);
36 }
37
GetForegroundTaskRunner(v8::Isolate * isolate)38 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
39 v8::Isolate* isolate) override {
40 return platform_->GetForegroundTaskRunner(isolate);
41 }
42
NumberOfWorkerThreads()43 int NumberOfWorkerThreads() override { return 0; }
44
CallOnWorkerThread(std::unique_ptr<Task> task)45 void CallOnWorkerThread(std::unique_ptr<Task> task) override {
46 // We post worker tasks on the foreground task runner of the
47 // {kProcessGlobalPredictablePlatformWorkerTaskQueue} isolate. The task
48 // queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue} isolate
49 // is then executed on the main thread to achieve predictable behavior.
50 //
51 // In this context here it is okay to call {GetForegroundTaskRunner} from a
52 // background thread. The reason is that code is executed sequentially with
53 // the PredictablePlatform, and that the {DefaultPlatform} does not access
54 // the isolate but only uses it as the key in a HashMap.
55 GetForegroundTaskRunner(kProcessGlobalPredictablePlatformWorkerTaskQueue)
56 ->PostTask(std::move(task));
57 }
58
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)59 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
60 double delay_in_seconds) override {
61 // Never run delayed tasks.
62 }
63
IdleTasksEnabled(Isolate * isolate)64 bool IdleTasksEnabled(Isolate* isolate) override { return false; }
65
PostJob(TaskPriority priority,std::unique_ptr<JobTask> job_task)66 std::unique_ptr<JobHandle> PostJob(
67 TaskPriority priority, std::unique_ptr<JobTask> job_task) override {
68 return platform_->PostJob(priority, std::move(job_task));
69 }
70
MonotonicallyIncreasingTime()71 double MonotonicallyIncreasingTime() override {
72 return synthetic_time_in_sec_ += 0.00001;
73 }
74
CurrentClockTimeMillis()75 double CurrentClockTimeMillis() override {
76 return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
77 }
78
GetTracingController()79 v8::TracingController* GetTracingController() override {
80 return platform_->GetTracingController();
81 }
82
platform() const83 Platform* platform() const { return platform_.get(); }
84
85 private:
86 double synthetic_time_in_sec_ = 0.0;
87 std::unique_ptr<Platform> platform_;
88
89 DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
90 };
91
MakePredictablePlatform(std::unique_ptr<Platform> platform)92 std::unique_ptr<Platform> MakePredictablePlatform(
93 std::unique_ptr<Platform> platform) {
94 return std::make_unique<PredictablePlatform>(std::move(platform));
95 }
96
97 class DelayedTasksPlatform final : public Platform {
98 public:
DelayedTasksPlatform(std::unique_ptr<Platform> platform)99 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform)
100 : platform_(std::move(platform)) {
101 DCHECK_NOT_NULL(platform_);
102 }
103
DelayedTasksPlatform(std::unique_ptr<Platform> platform,int64_t random_seed)104 explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform,
105 int64_t random_seed)
106 : platform_(std::move(platform)), rng_(random_seed) {
107 DCHECK_NOT_NULL(platform_);
108 }
109
~DelayedTasksPlatform()110 ~DelayedTasksPlatform() override {
111 // When the platform shuts down, all task runners must be freed.
112 DCHECK_EQ(0, delayed_task_runners_.size());
113 }
114
GetPageAllocator()115 PageAllocator* GetPageAllocator() override {
116 return platform_->GetPageAllocator();
117 }
118
OnCriticalMemoryPressure()119 void OnCriticalMemoryPressure() override {
120 platform_->OnCriticalMemoryPressure();
121 }
122
OnCriticalMemoryPressure(size_t length)123 bool OnCriticalMemoryPressure(size_t length) override {
124 return platform_->OnCriticalMemoryPressure(length);
125 }
126
GetForegroundTaskRunner(v8::Isolate * isolate)127 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
128 v8::Isolate* isolate) override {
129 std::shared_ptr<TaskRunner> runner =
130 platform_->GetForegroundTaskRunner(isolate);
131
132 base::MutexGuard lock_guard(&mutex_);
133 // Check if we can re-materialize the weak ptr in our map.
134 std::weak_ptr<DelayedTaskRunner>& weak_delayed_runner =
135 delayed_task_runners_[runner.get()];
136 std::shared_ptr<DelayedTaskRunner> delayed_runner =
137 weak_delayed_runner.lock();
138
139 if (!delayed_runner) {
140 // Create a new {DelayedTaskRunner} and keep a weak reference in our map.
141 delayed_runner.reset(new DelayedTaskRunner(runner, this),
142 DelayedTaskRunnerDeleter{});
143 weak_delayed_runner = delayed_runner;
144 }
145
146 return std::move(delayed_runner);
147 }
148
NumberOfWorkerThreads()149 int NumberOfWorkerThreads() override {
150 return platform_->NumberOfWorkerThreads();
151 }
152
CallOnWorkerThread(std::unique_ptr<Task> task)153 void CallOnWorkerThread(std::unique_ptr<Task> task) override {
154 platform_->CallOnWorkerThread(MakeDelayedTask(std::move(task)));
155 }
156
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)157 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
158 double delay_in_seconds) override {
159 platform_->CallDelayedOnWorkerThread(MakeDelayedTask(std::move(task)),
160 delay_in_seconds);
161 }
162
IdleTasksEnabled(Isolate * isolate)163 bool IdleTasksEnabled(Isolate* isolate) override {
164 return platform_->IdleTasksEnabled(isolate);
165 }
166
PostJob(TaskPriority priority,std::unique_ptr<JobTask> job_task)167 std::unique_ptr<JobHandle> PostJob(
168 TaskPriority priority, std::unique_ptr<JobTask> job_task) override {
169 return platform_->PostJob(priority, MakeDelayedJob(std::move(job_task)));
170 }
171
MonotonicallyIncreasingTime()172 double MonotonicallyIncreasingTime() override {
173 return platform_->MonotonicallyIncreasingTime();
174 }
175
CurrentClockTimeMillis()176 double CurrentClockTimeMillis() override {
177 return platform_->CurrentClockTimeMillis();
178 }
179
GetTracingController()180 v8::TracingController* GetTracingController() override {
181 return platform_->GetTracingController();
182 }
183
184 private:
185 class DelayedTaskRunnerDeleter;
186 class DelayedTaskRunner final : public TaskRunner {
187 public:
DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner,DelayedTasksPlatform * platform)188 DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner,
189 DelayedTasksPlatform* platform)
190 : task_runner_(task_runner), platform_(platform) {}
191
PostTask(std::unique_ptr<Task> task)192 void PostTask(std::unique_ptr<Task> task) final {
193 task_runner_->PostTask(platform_->MakeDelayedTask(std::move(task)));
194 }
195
PostNonNestableTask(std::unique_ptr<Task> task)196 void PostNonNestableTask(std::unique_ptr<Task> task) final {
197 task_runner_->PostNonNestableTask(
198 platform_->MakeDelayedTask(std::move(task)));
199 }
200
PostDelayedTask(std::unique_ptr<Task> task,double delay_in_seconds)201 void PostDelayedTask(std::unique_ptr<Task> task,
202 double delay_in_seconds) final {
203 task_runner_->PostDelayedTask(platform_->MakeDelayedTask(std::move(task)),
204 delay_in_seconds);
205 }
206
PostIdleTask(std::unique_ptr<IdleTask> task)207 void PostIdleTask(std::unique_ptr<IdleTask> task) final {
208 task_runner_->PostIdleTask(
209 platform_->MakeDelayedIdleTask(std::move(task)));
210 }
211
IdleTasksEnabled()212 bool IdleTasksEnabled() final { return task_runner_->IdleTasksEnabled(); }
213
NonNestableTasksEnabled() const214 bool NonNestableTasksEnabled() const final {
215 return task_runner_->NonNestableTasksEnabled();
216 }
217
218 private:
219 friend class DelayedTaskRunnerDeleter;
220 std::shared_ptr<TaskRunner> task_runner_;
221 DelayedTasksPlatform* platform_;
222 };
223
224 class DelayedTaskRunnerDeleter {
225 public:
operator ()(DelayedTaskRunner * runner) const226 void operator()(DelayedTaskRunner* runner) const {
227 TaskRunner* original_runner = runner->task_runner_.get();
228 base::MutexGuard lock_guard(&runner->platform_->mutex_);
229 auto& delayed_task_runners = runner->platform_->delayed_task_runners_;
230 DCHECK_EQ(1, delayed_task_runners.count(original_runner));
231 delayed_task_runners.erase(original_runner);
232 }
233 };
234
235 class DelayedTask final : public Task {
236 public:
DelayedTask(std::unique_ptr<Task> task,int32_t delay_ms)237 DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms)
238 : task_(std::move(task)), delay_ms_(delay_ms) {}
239
Run()240 void Run() override {
241 base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
242 task_->Run();
243 }
244
245 private:
246 std::unique_ptr<Task> task_;
247 int32_t delay_ms_;
248 };
249
250 class DelayedIdleTask final : public IdleTask {
251 public:
DelayedIdleTask(std::unique_ptr<IdleTask> task,int32_t delay_ms)252 DelayedIdleTask(std::unique_ptr<IdleTask> task, int32_t delay_ms)
253 : task_(std::move(task)), delay_ms_(delay_ms) {}
254
Run(double deadline_in_seconds)255 void Run(double deadline_in_seconds) override {
256 base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
257 task_->Run(deadline_in_seconds);
258 }
259
260 private:
261 std::unique_ptr<IdleTask> task_;
262 int32_t delay_ms_;
263 };
264
265 class DelayedJob final : public JobTask {
266 public:
DelayedJob(std::unique_ptr<JobTask> job_task,int32_t delay_ms)267 DelayedJob(std::unique_ptr<JobTask> job_task, int32_t delay_ms)
268 : job_task_(std::move(job_task)), delay_ms_(delay_ms) {}
269
Run(JobDelegate * delegate)270 void Run(JobDelegate* delegate) override {
271 // If this job is being executed via worker tasks (as e.g. the
272 // {DefaultJobHandle} implementation does it), the worker task would
273 // already include a delay. In order to not depend on that, we add our own
274 // delay here anyway.
275 base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_));
276 job_task_->Run(delegate);
277 }
278
GetMaxConcurrency(size_t worker_count) const279 size_t GetMaxConcurrency(size_t worker_count) const override {
280 return job_task_->GetMaxConcurrency(worker_count);
281 }
282
283 private:
284 std::unique_ptr<JobTask> job_task_;
285 int32_t delay_ms_;
286 };
287
288 std::unique_ptr<Platform> platform_;
289
290 // The Mutex protects the RNG, which is used by foreground and background
291 // threads, and the {delayed_task_runners_} map might be accessed concurrently
292 // by the shared_ptr destructor.
293 base::Mutex mutex_;
294 base::RandomNumberGenerator rng_;
295 std::unordered_map<TaskRunner*, std::weak_ptr<DelayedTaskRunner>>
296 delayed_task_runners_;
297
GetRandomDelayInMilliseconds()298 int32_t GetRandomDelayInMilliseconds() {
299 base::MutexGuard lock_guard(&mutex_);
300 double delay_fraction = rng_.NextDouble();
301 // Sleep up to 100ms (100000us). Square {delay_fraction} to shift
302 // distribution towards shorter sleeps.
303 return 1e5 * (delay_fraction * delay_fraction);
304 }
305
MakeDelayedTask(std::unique_ptr<Task> task)306 std::unique_ptr<Task> MakeDelayedTask(std::unique_ptr<Task> task) {
307 return std::make_unique<DelayedTask>(std::move(task),
308 GetRandomDelayInMilliseconds());
309 }
310
MakeDelayedIdleTask(std::unique_ptr<IdleTask> task)311 std::unique_ptr<IdleTask> MakeDelayedIdleTask(
312 std::unique_ptr<IdleTask> task) {
313 return std::make_unique<DelayedIdleTask>(std::move(task),
314 GetRandomDelayInMilliseconds());
315 }
316
MakeDelayedJob(std::unique_ptr<JobTask> task)317 std::unique_ptr<JobTask> MakeDelayedJob(std::unique_ptr<JobTask> task) {
318 return std::make_unique<DelayedJob>(std::move(task),
319 GetRandomDelayInMilliseconds());
320 }
321
322 DISALLOW_COPY_AND_ASSIGN(DelayedTasksPlatform);
323 };
324
MakeDelayedTasksPlatform(std::unique_ptr<Platform> platform,int64_t random_seed)325 std::unique_ptr<Platform> MakeDelayedTasksPlatform(
326 std::unique_ptr<Platform> platform, int64_t random_seed) {
327 if (random_seed) {
328 return std::make_unique<DelayedTasksPlatform>(std::move(platform),
329 random_seed);
330 }
331 return std::make_unique<DelayedTasksPlatform>(std::move(platform));
332 }
333
334 } // namespace v8
335