| // Copyright 2019 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 "src/libplatform/default-worker-threads-task-runner.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "include/v8-platform.h" |
| #include "src/base/platform/platform.h" |
| #include "src/base/platform/semaphore.h" |
| #include "src/base/platform/time.h" |
| #include "testing/gtest-support.h" |
| |
| namespace v8 { |
| namespace platform { |
| |
| class TestTask : public v8::Task { |
| public: |
| explicit TestTask(std::function<void()> f) : f_(std::move(f)) {} |
| |
| void Run() override { f_(); } |
| |
| private: |
| std::function<void()> f_; |
| }; |
| |
| double RealTime() { |
| return base::TimeTicks::HighResolutionNow().ToInternalValue() / |
| static_cast<double>(base::Time::kMicrosecondsPerSecond); |
| } |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostTaskOrder) { |
| DefaultWorkerThreadsTaskRunner runner(1, RealTime); |
| |
| std::vector<int> order; |
| base::Semaphore semaphore(0); |
| |
| std::unique_ptr<TestTask> task1 = |
| std::make_unique<TestTask>([&] { order.push_back(1); }); |
| std::unique_ptr<TestTask> task2 = |
| std::make_unique<TestTask>([&] { order.push_back(2); }); |
| std::unique_ptr<TestTask> task3 = std::make_unique<TestTask>([&] { |
| order.push_back(3); |
| semaphore.Signal(); |
| }); |
| |
| runner.PostTask(std::move(task1)); |
| runner.PostTask(std::move(task2)); |
| runner.PostTask(std::move(task3)); |
| |
| semaphore.Wait(); |
| |
| runner.Terminate(); |
| ASSERT_EQ(3UL, order.size()); |
| ASSERT_EQ(1, order[0]); |
| ASSERT_EQ(2, order[1]); |
| ASSERT_EQ(3, order[2]); |
| } |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostTaskOrderMultipleWorkers) { |
| DefaultWorkerThreadsTaskRunner runner(4, RealTime); |
| |
| base::Mutex vector_lock; |
| std::vector<int> order; |
| std::atomic_int count{0}; |
| |
| std::unique_ptr<TestTask> task1 = std::make_unique<TestTask>([&] { |
| base::MutexGuard guard(&vector_lock); |
| order.push_back(1); |
| count++; |
| }); |
| std::unique_ptr<TestTask> task2 = std::make_unique<TestTask>([&] { |
| base::MutexGuard guard(&vector_lock); |
| order.push_back(2); |
| count++; |
| }); |
| std::unique_ptr<TestTask> task3 = std::make_unique<TestTask>([&] { |
| base::MutexGuard guard(&vector_lock); |
| order.push_back(3); |
| count++; |
| }); |
| std::unique_ptr<TestTask> task4 = std::make_unique<TestTask>([&] { |
| base::MutexGuard guard(&vector_lock); |
| order.push_back(4); |
| count++; |
| }); |
| std::unique_ptr<TestTask> task5 = std::make_unique<TestTask>([&] { |
| base::MutexGuard guard(&vector_lock); |
| order.push_back(5); |
| count++; |
| }); |
| |
| runner.PostTask(std::move(task1)); |
| runner.PostTask(std::move(task2)); |
| runner.PostTask(std::move(task3)); |
| runner.PostTask(std::move(task4)); |
| runner.PostTask(std::move(task5)); |
| |
| // We can't observe any ordering when there are multiple worker threads. The |
| // tasks are guaranteed to be dispatched to workers in the input order, but |
| // the workers are different threads and can be scheduled arbitrarily. Just |
| // check that all of the tasks were run once. |
| while (count != 5) { |
| } |
| |
| runner.Terminate(); |
| ASSERT_EQ(5UL, order.size()); |
| ASSERT_EQ(1, std::count(order.begin(), order.end(), 1)); |
| ASSERT_EQ(1, std::count(order.begin(), order.end(), 2)); |
| ASSERT_EQ(1, std::count(order.begin(), order.end(), 3)); |
| ASSERT_EQ(1, std::count(order.begin(), order.end(), 4)); |
| ASSERT_EQ(1, std::count(order.begin(), order.end(), 5)); |
| } |
| |
| class FakeClock { |
| public: |
| static double time() { return time_.load(); } |
| static void set_time(double time) { time_.store(time); } |
| static void set_time_and_wake_up_runner( |
| double time, DefaultWorkerThreadsTaskRunner* runner) { |
| time_.store(time); |
| // PostTask will cause the condition variable WaitFor() call to be notified |
| // early, rather than waiting for the real amount of time. WaitFor() listens |
| // to the system clock and not our FakeClock. |
| runner->PostTask(std::make_unique<TestTask>([] {})); |
| } |
| |
| private: |
| static std::atomic<double> time_; |
| }; |
| |
| std::atomic<double> FakeClock::time_{0.0}; |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostDelayedTaskOrder) { |
| FakeClock::set_time(0.0); |
| DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time); |
| |
| std::vector<int> order; |
| base::Semaphore task1_semaphore(0); |
| base::Semaphore task3_semaphore(0); |
| |
| std::unique_ptr<TestTask> task1 = std::make_unique<TestTask>([&] { |
| order.push_back(1); |
| task1_semaphore.Signal(); |
| }); |
| std::unique_ptr<TestTask> task2 = |
| std::make_unique<TestTask>([&] { order.push_back(2); }); |
| std::unique_ptr<TestTask> task3 = std::make_unique<TestTask>([&] { |
| order.push_back(3); |
| task3_semaphore.Signal(); |
| }); |
| |
| runner.PostDelayedTask(std::move(task1), 100); |
| runner.PostTask(std::move(task2)); |
| runner.PostTask(std::move(task3)); |
| |
| FakeClock::set_time_and_wake_up_runner(99, &runner); |
| |
| task3_semaphore.Wait(); |
| ASSERT_EQ(2UL, order.size()); |
| ASSERT_EQ(2, order[0]); |
| ASSERT_EQ(3, order[1]); |
| |
| FakeClock::set_time_and_wake_up_runner(101, &runner); |
| task1_semaphore.Wait(); |
| |
| runner.Terminate(); |
| ASSERT_EQ(3UL, order.size()); |
| ASSERT_EQ(2, order[0]); |
| ASSERT_EQ(3, order[1]); |
| ASSERT_EQ(1, order[2]); |
| } |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostDelayedTaskOrder2) { |
| FakeClock::set_time(0.0); |
| DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time); |
| |
| std::vector<int> order; |
| base::Semaphore task1_semaphore(0); |
| base::Semaphore task2_semaphore(0); |
| base::Semaphore task3_semaphore(0); |
| |
| std::unique_ptr<TestTask> task1 = std::make_unique<TestTask>([&] { |
| order.push_back(1); |
| task1_semaphore.Signal(); |
| }); |
| std::unique_ptr<TestTask> task2 = std::make_unique<TestTask>([&] { |
| order.push_back(2); |
| task2_semaphore.Signal(); |
| }); |
| std::unique_ptr<TestTask> task3 = std::make_unique<TestTask>([&] { |
| order.push_back(3); |
| task3_semaphore.Signal(); |
| }); |
| |
| runner.PostDelayedTask(std::move(task1), 500); |
| runner.PostDelayedTask(std::move(task2), 100); |
| runner.PostDelayedTask(std::move(task3), 200); |
| |
| FakeClock::set_time_and_wake_up_runner(101, &runner); |
| |
| task2_semaphore.Wait(); |
| ASSERT_EQ(1UL, order.size()); |
| ASSERT_EQ(2, order[0]); |
| |
| FakeClock::set_time_and_wake_up_runner(201, &runner); |
| |
| task3_semaphore.Wait(); |
| ASSERT_EQ(2UL, order.size()); |
| ASSERT_EQ(2, order[0]); |
| ASSERT_EQ(3, order[1]); |
| |
| FakeClock::set_time_and_wake_up_runner(501, &runner); |
| |
| task1_semaphore.Wait(); |
| runner.Terminate(); |
| ASSERT_EQ(3UL, order.size()); |
| ASSERT_EQ(2, order[0]); |
| ASSERT_EQ(3, order[1]); |
| ASSERT_EQ(1, order[2]); |
| } |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, PostAfterTerminate) { |
| FakeClock::set_time(0.0); |
| DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time); |
| |
| std::vector<int> order; |
| base::Semaphore task1_semaphore(0); |
| base::Semaphore task2_semaphore(0); |
| base::Semaphore task3_semaphore(0); |
| |
| std::unique_ptr<TestTask> task1 = std::make_unique<TestTask>([&] { |
| order.push_back(1); |
| task1_semaphore.Signal(); |
| }); |
| std::unique_ptr<TestTask> task2 = std::make_unique<TestTask>([&] { |
| order.push_back(2); |
| task2_semaphore.Signal(); |
| }); |
| std::unique_ptr<TestTask> task3 = std::make_unique<TestTask>([&] { |
| order.push_back(3); |
| task3_semaphore.Signal(); |
| }); |
| |
| runner.PostTask(std::move(task1)); |
| runner.PostDelayedTask(std::move(task2), 100); |
| |
| task1_semaphore.Wait(); |
| ASSERT_EQ(1UL, order.size()); |
| ASSERT_EQ(1, order[0]); |
| |
| runner.Terminate(); |
| FakeClock::set_time_and_wake_up_runner(201, &runner); |
| // OK, we can't actually prove that this never executes. But wait a bit at |
| // least. |
| bool signalled = |
| task2_semaphore.WaitFor(base::TimeDelta::FromMilliseconds(100)); |
| ASSERT_FALSE(signalled); |
| ASSERT_EQ(1UL, order.size()); |
| ASSERT_EQ(1, order[0]); |
| |
| runner.PostTask(std::move(task3)); |
| signalled = task3_semaphore.WaitFor(base::TimeDelta::FromMilliseconds(100)); |
| ASSERT_FALSE(signalled); |
| ASSERT_EQ(1UL, order.size()); |
| ASSERT_EQ(1, order[0]); |
| } |
| |
| TEST(DefaultWorkerThreadsTaskRunnerUnittest, NoIdleTasks) { |
| DefaultWorkerThreadsTaskRunner runner(1, FakeClock::time); |
| |
| ASSERT_FALSE(runner.IdleTasksEnabled()); |
| runner.Terminate(); |
| } |
| |
| } // namespace platform |
| } // namespace v8 |