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