| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <unordered_map> |
| |
| #include "include/v8-platform.h" |
| #include "src/base/logging.h" |
| #include "src/base/macros.h" |
| #include "src/base/platform/mutex.h" |
| #include "src/base/platform/platform.h" |
| #include "src/base/platform/time.h" |
| #include "src/base/template-utils.h" |
| #include "src/base/utils/random-number-generator.h" |
| #include "src/d8/d8-platforms.h" |
| |
| namespace v8 { |
| |
| class PredictablePlatform : public Platform { |
| public: |
| explicit PredictablePlatform(std::unique_ptr<Platform> platform) |
| : platform_(std::move(platform)) { |
| DCHECK_NOT_NULL(platform_); |
| } |
| |
| PageAllocator* GetPageAllocator() override { |
| return platform_->GetPageAllocator(); |
| } |
| |
| void OnCriticalMemoryPressure() override { |
| platform_->OnCriticalMemoryPressure(); |
| } |
| |
| bool OnCriticalMemoryPressure(size_t length) override { |
| return platform_->OnCriticalMemoryPressure(length); |
| } |
| |
| std::shared_ptr<TaskRunner> GetForegroundTaskRunner( |
| v8::Isolate* isolate) override { |
| return platform_->GetForegroundTaskRunner(isolate); |
| } |
| |
| int NumberOfWorkerThreads() override { return 0; } |
| |
| void CallOnWorkerThread(std::unique_ptr<Task> task) override { |
| // It's not defined when background tasks are being executed, so we can just |
| // execute them right away. |
| task->Run(); |
| } |
| |
| void CallDelayedOnWorkerThread(std::unique_ptr<Task> task, |
| double delay_in_seconds) override { |
| // Never run delayed tasks. |
| } |
| |
| void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { |
| // This is a deprecated function and should not be called anymore. |
| UNREACHABLE(); |
| } |
| |
| void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, |
| double delay_in_seconds) override { |
| // This is a deprecated function and should not be called anymore. |
| UNREACHABLE(); |
| } |
| |
| void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override { |
| UNREACHABLE(); |
| } |
| |
| bool IdleTasksEnabled(Isolate* isolate) override { return false; } |
| |
| double MonotonicallyIncreasingTime() override { |
| return synthetic_time_in_sec_ += 0.00001; |
| } |
| |
| double CurrentClockTimeMillis() override { |
| return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond; |
| } |
| |
| v8::TracingController* GetTracingController() override { |
| return platform_->GetTracingController(); |
| } |
| |
| Platform* platform() const { return platform_.get(); } |
| |
| private: |
| double synthetic_time_in_sec_ = 0.0; |
| std::unique_ptr<Platform> platform_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PredictablePlatform); |
| }; |
| |
| std::unique_ptr<Platform> MakePredictablePlatform( |
| std::unique_ptr<Platform> platform) { |
| return base::make_unique<PredictablePlatform>(std::move(platform)); |
| } |
| |
| class DelayedTasksPlatform : public Platform { |
| public: |
| explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform) |
| : platform_(std::move(platform)) { |
| DCHECK_NOT_NULL(platform_); |
| } |
| |
| explicit DelayedTasksPlatform(std::unique_ptr<Platform> platform, |
| int64_t random_seed) |
| : platform_(std::move(platform)), rng_(random_seed) { |
| DCHECK_NOT_NULL(platform_); |
| } |
| |
| ~DelayedTasksPlatform() { |
| // When the platform shuts down, all task runners must be freed. |
| DCHECK_EQ(0, delayed_task_runners_.size()); |
| } |
| |
| PageAllocator* GetPageAllocator() override { |
| return platform_->GetPageAllocator(); |
| } |
| |
| void OnCriticalMemoryPressure() override { |
| platform_->OnCriticalMemoryPressure(); |
| } |
| |
| bool OnCriticalMemoryPressure(size_t length) override { |
| return platform_->OnCriticalMemoryPressure(length); |
| } |
| |
| std::shared_ptr<TaskRunner> GetForegroundTaskRunner( |
| v8::Isolate* isolate) override { |
| std::shared_ptr<TaskRunner> runner = |
| platform_->GetForegroundTaskRunner(isolate); |
| |
| base::MutexGuard lock_guard(&mutex_); |
| // Check if we can re-materialize the weak ptr in our map. |
| std::weak_ptr<DelayedTaskRunner>& weak_delayed_runner = |
| delayed_task_runners_[runner.get()]; |
| std::shared_ptr<DelayedTaskRunner> delayed_runner = |
| weak_delayed_runner.lock(); |
| |
| if (!delayed_runner) { |
| // Create a new {DelayedTaskRunner} and keep a weak reference in our map. |
| delayed_runner.reset(new DelayedTaskRunner(runner, this), |
| DelayedTaskRunnerDeleter{}); |
| weak_delayed_runner = delayed_runner; |
| } |
| |
| return std::move(delayed_runner); |
| } |
| |
| int NumberOfWorkerThreads() override { |
| return platform_->NumberOfWorkerThreads(); |
| } |
| |
| void CallOnWorkerThread(std::unique_ptr<Task> task) override { |
| platform_->CallOnWorkerThread(MakeDelayedTask(std::move(task))); |
| } |
| |
| void CallDelayedOnWorkerThread(std::unique_ptr<Task> task, |
| double delay_in_seconds) override { |
| platform_->CallDelayedOnWorkerThread(MakeDelayedTask(std::move(task)), |
| delay_in_seconds); |
| } |
| |
| void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { |
| // This is a deprecated function and should not be called anymore. |
| UNREACHABLE(); |
| } |
| |
| void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, |
| double delay_in_seconds) override { |
| // This is a deprecated function and should not be called anymore. |
| UNREACHABLE(); |
| } |
| |
| void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override { |
| // This is a deprecated function and should not be called anymore. |
| UNREACHABLE(); |
| } |
| |
| bool IdleTasksEnabled(Isolate* isolate) override { |
| return platform_->IdleTasksEnabled(isolate); |
| } |
| |
| double MonotonicallyIncreasingTime() override { |
| return platform_->MonotonicallyIncreasingTime(); |
| } |
| |
| double CurrentClockTimeMillis() override { |
| return platform_->CurrentClockTimeMillis(); |
| } |
| |
| v8::TracingController* GetTracingController() override { |
| return platform_->GetTracingController(); |
| } |
| |
| private: |
| class DelayedTaskRunnerDeleter; |
| class DelayedTaskRunner final : public TaskRunner { |
| public: |
| DelayedTaskRunner(std::shared_ptr<TaskRunner> task_runner, |
| DelayedTasksPlatform* platform) |
| : task_runner_(task_runner), platform_(platform) {} |
| |
| void PostTask(std::unique_ptr<Task> task) final { |
| task_runner_->PostTask(platform_->MakeDelayedTask(std::move(task))); |
| } |
| |
| void PostDelayedTask(std::unique_ptr<Task> task, |
| double delay_in_seconds) final { |
| task_runner_->PostDelayedTask(platform_->MakeDelayedTask(std::move(task)), |
| delay_in_seconds); |
| } |
| |
| void PostIdleTask(std::unique_ptr<IdleTask> task) final { |
| task_runner_->PostIdleTask( |
| platform_->MakeDelayedIdleTask(std::move(task))); |
| } |
| |
| bool IdleTasksEnabled() final { return task_runner_->IdleTasksEnabled(); } |
| |
| private: |
| friend class DelayedTaskRunnerDeleter; |
| std::shared_ptr<TaskRunner> task_runner_; |
| DelayedTasksPlatform* platform_; |
| }; |
| |
| class DelayedTaskRunnerDeleter { |
| public: |
| void operator()(DelayedTaskRunner* runner) const { |
| TaskRunner* original_runner = runner->task_runner_.get(); |
| base::MutexGuard lock_guard(&runner->platform_->mutex_); |
| auto& delayed_task_runners = runner->platform_->delayed_task_runners_; |
| DCHECK_EQ(1, delayed_task_runners.count(original_runner)); |
| delayed_task_runners.erase(original_runner); |
| } |
| }; |
| |
| class DelayedTask : public Task { |
| public: |
| DelayedTask(std::unique_ptr<Task> task, int32_t delay_ms) |
| : task_(std::move(task)), delay_ms_(delay_ms) {} |
| void Run() final { |
| base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_)); |
| task_->Run(); |
| } |
| |
| private: |
| std::unique_ptr<Task> task_; |
| int32_t delay_ms_; |
| }; |
| |
| class DelayedIdleTask : public IdleTask { |
| public: |
| DelayedIdleTask(std::unique_ptr<IdleTask> task, int32_t delay_ms) |
| : task_(std::move(task)), delay_ms_(delay_ms) {} |
| void Run(double deadline_in_seconds) final { |
| base::OS::Sleep(base::TimeDelta::FromMicroseconds(delay_ms_)); |
| task_->Run(deadline_in_seconds); |
| } |
| |
| private: |
| std::unique_ptr<IdleTask> task_; |
| int32_t delay_ms_; |
| }; |
| |
| std::unique_ptr<Platform> platform_; |
| |
| // The Mutex protects the RNG, which is used by foreground and background |
| // threads, and the {delayed_task_runners_} map might be accessed concurrently |
| // by the shared_ptr destructor. |
| base::Mutex mutex_; |
| base::RandomNumberGenerator rng_; |
| std::unordered_map<TaskRunner*, std::weak_ptr<DelayedTaskRunner>> |
| delayed_task_runners_; |
| |
| int32_t GetRandomDelayInMilliseconds() { |
| base::MutexGuard lock_guard(&mutex_); |
| double delay_fraction = rng_.NextDouble(); |
| // Sleep up to 100ms (100000us). Square {delay_fraction} to shift |
| // distribution towards shorter sleeps. |
| return 1e5 * (delay_fraction * delay_fraction); |
| } |
| |
| std::unique_ptr<Task> MakeDelayedTask(std::unique_ptr<Task> task) { |
| return base::make_unique<DelayedTask>(std::move(task), |
| GetRandomDelayInMilliseconds()); |
| } |
| |
| std::unique_ptr<IdleTask> MakeDelayedIdleTask( |
| std::unique_ptr<IdleTask> task) { |
| return base::make_unique<DelayedIdleTask>(std::move(task), |
| GetRandomDelayInMilliseconds()); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(DelayedTasksPlatform); |
| }; |
| |
| std::unique_ptr<Platform> MakeDelayedTasksPlatform( |
| std::unique_ptr<Platform> platform, int64_t random_seed) { |
| if (random_seed) { |
| return base::make_unique<DelayedTasksPlatform>(std::move(platform), |
| random_seed); |
| } |
| return base::make_unique<DelayedTasksPlatform>(std::move(platform)); |
| } |
| |
| } // namespace v8 |