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 | |
18 | namespace v8 { |
19 | |
20 | class 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 | |
95 | std::unique_ptr<Platform> MakePredictablePlatform( |
96 | std::unique_ptr<Platform> platform) { |
97 | return base::make_unique<PredictablePlatform>(std::move(platform)); |
98 | } |
99 | |
100 | class 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 | |
300 | std::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 | |