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