cb5dfb7d73
TimeTicks::HighResolutionNow is identical to TimeTicks::Now since 2018 (https://crrev.com/c/997153), but the declaration still has a wrong comment about a non-existing DCHECK. In order to avoid confusion, remove the redundant method and just use TimeTicks::Now everywhere. Drive-by: Make IsHighResolutionTimer "inline" instead of "V8_INLINE" because it will only be called once anyway. R=mlippautz@chromium.org Bug: v8:12425 Change-Id: I31dc65f8c1ac910862e070e60e928054d4921154 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3439909 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/main@{#78944}
280 lines
8.0 KiB
C++
280 lines
8.0 KiB
C++
// 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::Now().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
|