blob: 6fc13ee4c9ff5a4a91e779eaa834e434b43326ad [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/timer.h"
#include <memory>
#include <queue>
#include "base/memory/raw_ptr.h"
#include "base/task/common/lazy_now.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
using base::sequence_manager::TaskQueue;
using blink::scheduler::MainThreadTaskQueue;
using testing::ElementsAre;
namespace blink {
namespace {
class TimerTest : public testing::Test {
public:
TimerTest() {
scoped_refptr<MainThreadTaskQueue> task_queue =
platform_->GetMainThreadScheduler()->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kTest));
task_runner_ = task_queue->CreateTaskRunner(TaskType::kInternalTest);
}
void SetUp() override {
run_times_.clear();
platform_->AdvanceClock(base::Seconds(10));
start_time_ = Now();
}
base::TimeTicks Now() { return platform_->test_task_runner()->NowTicks(); }
void CountingTask(TimerBase*) { run_times_.push_back(Now()); }
void RecordNextFireTimeTask(TimerBase* timer) {
next_fire_times_.push_back(Now() + timer->NextFireInterval());
}
void RunUntilDeadline(base::TimeTicks deadline) {
base::TimeDelta period = deadline - Now();
EXPECT_GE(period, base::TimeDelta());
platform_->RunForPeriod(period);
}
// Returns false if there are no pending delayed tasks, otherwise sets |time|
// to the delay in seconds till the next pending delayed task is scheduled to
// fire.
bool TimeTillNextDelayedTask(base::TimeDelta* time) const {
base::LazyNow lazy_now(platform_->NowTicks());
auto* scheduler_helper =
platform_->GetMainThreadScheduler()->GetSchedulerHelperForTesting();
scheduler_helper->ReclaimMemory();
auto wake_up = scheduler_helper->GetNextWakeUp();
if (!wake_up)
return false;
*time = wake_up->time - lazy_now.Now();
return true;
}
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
return task_runner_;
}
protected:
base::TimeTicks start_time_;
WTF::Vector<base::TimeTicks> run_times_;
WTF::Vector<base::TimeTicks> next_fire_times_;
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
base::test::TaskEnvironment task_environment_;
};
class OnHeapTimerOwner final : public GarbageCollected<OnHeapTimerOwner> {
public:
class Record final : public RefCounted<Record> {
public:
static scoped_refptr<Record> Create() { return base::AdoptRef(new Record); }
bool TimerHasFired() const { return timer_has_fired_; }
bool IsDisposed() const { return is_disposed_; }
bool OwnerIsDestructed() const { return owner_is_destructed_; }
void SetTimerHasFired() { timer_has_fired_ = true; }
void Dispose() { is_disposed_ = true; }
void SetOwnerIsDestructed() { owner_is_destructed_ = true; }
private:
Record() = default;
bool timer_has_fired_ = false;
bool is_disposed_ = false;
bool owner_is_destructed_ = false;
};
explicit OnHeapTimerOwner(
scoped_refptr<Record> record,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: timer_(std::move(task_runner), this, &OnHeapTimerOwner::Fired),
record_(std::move(record)) {}
~OnHeapTimerOwner() { record_->SetOwnerIsDestructed(); }
void StartOneShot(base::TimeDelta interval, const base::Location& caller) {
timer_.StartOneShot(interval, caller);
}
void Trace(Visitor* visitor) const { visitor->Trace(timer_); }
private:
void Fired(TimerBase*) {
EXPECT_FALSE(record_->IsDisposed());
record_->SetTimerHasFired();
}
HeapTaskRunnerTimer<OnHeapTimerOwner> timer_;
scoped_refptr<Record> record_;
};
TEST_F(TimerTest, StartOneShot_Zero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
base::TimeDelta run_time;
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_));
}
TEST_F(TimerTest, StartOneShot_ZeroAndCancel) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
base::TimeDelta run_time;
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
timer.Stop();
platform_->RunUntilIdle();
EXPECT_FALSE(run_times_.size());
}
TEST_F(TimerTest, StartOneShot_ZeroAndCancelThenRepost) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
base::TimeDelta run_time;
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
timer.Stop();
platform_->RunUntilIdle();
EXPECT_FALSE(run_times_.size());
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_));
}
TEST_F(TimerTest, StartOneShot_Zero_RepostingAfterRunning) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
base::TimeDelta run_time;
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_));
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_, start_time_));
}
TEST_F(TimerTest, StartOneShot_NonZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}
TEST_F(TimerTest, StartOneShot_NonZeroAndCancel) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
timer.Stop();
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_FALSE(run_times_.size());
}
TEST_F(TimerTest, StartOneShot_NonZeroAndCancelThenRepost) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
timer.Stop();
EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
platform_->RunUntilIdle();
EXPECT_FALSE(run_times_.size());
base::TimeTicks second_post_time = Now();
timer.StartOneShot(base::Seconds(10), FROM_HERE);
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(second_post_time + base::Seconds(10)));
}
TEST_F(TimerTest, StartOneShot_NonZero_RepostingAfterRunning) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
timer.StartOneShot(base::Seconds(20), FROM_HERE);
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(20), run_time);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10),
start_time_ + base::Seconds(30)));
}
TEST_F(TimerTest, PostingTimerTwiceWithSameRunTimeDoesNothing) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(10), run_time);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}
TEST_F(TimerTest, PostingTimerTwiceWithNewerRunTimeCancelsOriginalTask) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(0)));
}
TEST_F(TimerTest, PostingTimerTwiceWithLaterRunTimeCancelsOriginalTask) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}
TEST_F(TimerTest, StartRepeatingTask) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(1), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(1), run_time);
RunUntilDeadline(start_time_ + base::Milliseconds(5500));
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
start_time_ + base::Seconds(2),
start_time_ + base::Seconds(3),
start_time_ + base::Seconds(4),
start_time_ + base::Seconds(5)));
}
TEST_F(TimerTest, StartRepeatingTask_ThenCancel) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(1), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(1), run_time);
RunUntilDeadline(start_time_ + base::Milliseconds(2500));
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
start_time_ + base::Seconds(2)));
timer.Stop();
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
start_time_ + base::Seconds(2)));
}
TEST_F(TimerTest, StartRepeatingTask_ThenPostOneShot) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(1), FROM_HERE);
base::TimeDelta run_time;
EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
EXPECT_EQ(base::Seconds(1), run_time);
RunUntilDeadline(start_time_ + base::Milliseconds(2500));
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
start_time_ + base::Seconds(2)));
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
start_time_ + base::Seconds(2),
start_time_ + base::Milliseconds(2500)));
}
TEST_F(TimerTest, IsActive_NeverPosted) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
EXPECT_FALSE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterPosting_OneShotZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_TRUE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterPosting_OneShotNonZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
EXPECT_TRUE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterPosting_Repeating) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(1), FROM_HERE);
EXPECT_TRUE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterRunning_OneShotZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
platform_->RunUntilIdle();
EXPECT_FALSE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterRunning_OneShotNonZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
platform_->RunUntilIdle();
EXPECT_FALSE(timer.IsActive());
}
TEST_F(TimerTest, IsActive_AfterRunning_Repeating) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(1), FROM_HERE);
RunUntilDeadline(start_time_ + base::Seconds(10));
EXPECT_TRUE(timer.IsActive()); // It should run until cancelled.
}
TEST_F(TimerTest, NextFireInterval_OneShotZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_TRUE(timer.NextFireInterval().is_zero());
}
TEST_F(TimerTest, NextFireInterval_OneShotNonZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());
}
TEST_F(TimerTest, NextFireInterval_OneShotNonZero_AfterAFewSeconds) {
platform_->SetAutoAdvanceNowToPendingTasks(false);
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
platform_->AdvanceClock(base::Seconds(2));
EXPECT_EQ(base::Seconds(8), timer.NextFireInterval());
}
TEST_F(TimerTest, NextFireInterval_Repeating) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(20), FROM_HERE);
EXPECT_EQ(base::Seconds(20), timer.NextFireInterval());
}
TEST_F(TimerTest, RepeatInterval_NeverStarted) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
EXPECT_TRUE(timer.RepeatInterval().is_zero());
}
TEST_F(TimerTest, RepeatInterval_OneShotZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_TRUE(timer.RepeatInterval().is_zero());
}
TEST_F(TimerTest, RepeatInterval_OneShotNonZero) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartOneShot(base::Seconds(10), FROM_HERE);
EXPECT_TRUE(timer.RepeatInterval().is_zero());
}
TEST_F(TimerTest, RepeatInterval_Repeating) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(20), FROM_HERE);
EXPECT_EQ(base::Seconds(20), timer.RepeatInterval());
}
TEST_F(TimerTest, AugmentRepeatInterval) {
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(10), FROM_HERE);
EXPECT_EQ(base::Seconds(10), timer.RepeatInterval());
EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());
platform_->AdvanceClock(base::Seconds(2));
timer.AugmentRepeatInterval(base::Seconds(10));
EXPECT_EQ(base::Seconds(20), timer.RepeatInterval());
EXPECT_EQ(base::Seconds(18), timer.NextFireInterval());
RunUntilDeadline(start_time_ + base::Seconds(50));
EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(20),
start_time_ + base::Seconds(40)));
}
TEST_F(TimerTest, AugmentRepeatInterval_TimerFireDelayed) {
platform_->SetAutoAdvanceNowToPendingTasks(false);
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::CountingTask);
timer.StartRepeating(base::Seconds(10), FROM_HERE);
EXPECT_EQ(base::Seconds(10), timer.RepeatInterval());
EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());
platform_->AdvanceClock(base::Seconds(123)); // Make the timer long overdue.
timer.AugmentRepeatInterval(base::Seconds(10));
EXPECT_EQ(base::Seconds(20), timer.RepeatInterval());
// The timer is overdue so it should be scheduled to fire immediatly.
EXPECT_TRUE(timer.NextFireInterval().is_zero());
}
TEST_F(TimerTest, RepeatingTimerDoesNotDrift) {
platform_->SetAutoAdvanceNowToPendingTasks(false);
TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
&TimerTest::RecordNextFireTimeTask);
timer.StartRepeating(base::Seconds(2), FROM_HERE);
RecordNextFireTimeTask(
&timer); // Next scheduled task to run at |start_time_| + 2s
// Simulate timer firing early. Next scheduled task to run at
// |start_time_| + 4s
platform_->AdvanceClock(base::Milliseconds(1900));
RunUntilDeadline(Now() + base::Milliseconds(200));
// Next scheduled task to run at |start_time_| + 6s
platform_->RunForPeriod(base::Seconds(2));
// Next scheduled task to run at |start_time_| + 8s
platform_->RunForPeriod(base::Milliseconds(2100));
// Next scheduled task to run at |start_time_| + 10s
platform_->RunForPeriod(base::Milliseconds(2900));
// Next scheduled task to run at |start_time_| + 12s
platform_->AdvanceClock(base::Milliseconds(1800));
platform_->RunUntilIdle();
// Next scheduled task to run at |start_time_| + 14s
platform_->AdvanceClock(base::Milliseconds(1900));
platform_->RunUntilIdle();
// Next scheduled task to run at |start_time_| + 18s (skips a beat)
platform_->AdvanceClock(base::Milliseconds(50));
platform_->RunUntilIdle();
// Next scheduled task to run at |start_time_| + 28s (skips 5 beats)
platform_->AdvanceClock(base::Seconds(10));
platform_->RunUntilIdle();
EXPECT_THAT(
next_fire_times_,
ElementsAre(
start_time_ + base::Seconds(2), start_time_ + base::Seconds(4),
start_time_ + base::Seconds(6), start_time_ + base::Seconds(8),
start_time_ + base::Seconds(10), start_time_ + base::Seconds(12),
start_time_ + base::Seconds(14), start_time_ + base::Seconds(24)));
}
template <typename TimerFiredClass>
class TimerForTest : public TaskRunnerTimer<TimerFiredClass> {
public:
using TimerFiredFunction =
typename TaskRunnerTimer<TimerFiredClass>::TimerFiredFunction;
~TimerForTest() override = default;
TimerForTest(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
TimerFiredClass* timer_fired_class,
TimerFiredFunction timer_fired_function)
: TaskRunnerTimer<TimerFiredClass>(std::move(task_runner),
timer_fired_class,
timer_fired_function) {}
};
TEST_F(TimerTest, UserSuppliedTaskRunner) {
scoped_refptr<MainThreadTaskQueue> task_queue(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
task_queue->CreateTaskRunner(TaskType::kInternalTest);
TimerForTest<TimerTest> timer(task_runner, this, &TimerTest::CountingTask);
timer.StartOneShot(base::TimeDelta(), FROM_HERE);
// Make sure the task was posted on taskRunner.
EXPECT_FALSE(task_queue->IsEmpty());
}
TEST_F(TimerTest, RunOnHeapTimer) {
scoped_refptr<OnHeapTimerOwner::Record> record =
OnHeapTimerOwner::Record::Create();
Persistent<OnHeapTimerOwner> owner =
MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());
owner->StartOneShot(base::TimeDelta(), FROM_HERE);
EXPECT_FALSE(record->TimerHasFired());
platform_->RunUntilIdle();
EXPECT_TRUE(record->TimerHasFired());
}
TEST_F(TimerTest, DestructOnHeapTimer) {
scoped_refptr<OnHeapTimerOwner::Record> record =
OnHeapTimerOwner::Record::Create();
Persistent<OnHeapTimerOwner> owner =
MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());
record->Dispose();
owner->StartOneShot(base::TimeDelta(), FROM_HERE);
owner = nullptr;
ThreadState::Current()->CollectAllGarbageForTesting(
ThreadState::StackState::kNoHeapPointers);
EXPECT_TRUE(record->OwnerIsDestructed());
EXPECT_FALSE(record->TimerHasFired());
platform_->RunUntilIdle();
EXPECT_FALSE(record->TimerHasFired());
}
// TODO(1056170): Re-enable test.
TEST_F(TimerTest, DISABLED_MarkOnHeapTimerAsUnreachable) {
scoped_refptr<OnHeapTimerOwner::Record> record =
OnHeapTimerOwner::Record::Create();
Persistent<OnHeapTimerOwner> owner =
MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());
record->Dispose();
owner->StartOneShot(base::TimeDelta(), FROM_HERE);
owner = nullptr;
// Explicit regular GC call to allow lazy sweeping.
// TODO(1056170): Needs a specific forced GC call to be able to test the
// scenario below.
// ThreadState::Current()->CollectGarbageForTesting(
// BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack,
// BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping,
// BlinkGC::GCReason::kForcedGCForTesting);
// Since the heap is laziy swept, owner is not yet destructed.
EXPECT_FALSE(record->OwnerIsDestructed());
{
ThreadState::GCForbiddenScope gc_forbidden(ThreadState::Current());
EXPECT_FALSE(record->TimerHasFired());
platform_->RunUntilIdle();
EXPECT_FALSE(record->TimerHasFired());
EXPECT_FALSE(record->OwnerIsDestructed());
// ThreadState::Current()->CompleteSweep();
}
}
namespace {
class TaskObserver : public base::TaskObserver {
public:
TaskObserver(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
Vector<scoped_refptr<base::SingleThreadTaskRunner>>* run_order)
: task_runner_(std::move(task_runner)), run_order_(run_order) {}
void WillProcessTask(const base::PendingTask&, bool) override {}
void DidProcessTask(const base::PendingTask&) override {
run_order_->push_back(task_runner_);
}
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
raw_ptr<Vector<scoped_refptr<base::SingleThreadTaskRunner>>> run_order_;
};
} // namespace
TEST_F(TimerTest, MoveToNewTaskRunnerOneShot) {
Vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;
scoped_refptr<MainThreadTaskQueue> task_queue1(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
task_queue1->CreateTaskRunner(TaskType::kInternalTest);
TaskObserver task_observer1(task_runner1, &run_order);
task_queue1->AddTaskObserver(&task_observer1);
scoped_refptr<MainThreadTaskQueue> task_queue2(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
task_queue2->CreateTaskRunner(TaskType::kInternalTest);
TaskObserver task_observer2(task_runner2, &run_order);
task_queue2->AddTaskObserver(&task_observer2);
TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);
base::TimeTicks start_time = Now();
timer.StartOneShot(base::Seconds(1), FROM_HERE);
platform_->RunForPeriod(base::Milliseconds(500));
timer.MoveToNewTaskRunner(task_runner2);
platform_->RunUntilIdle();
EXPECT_THAT(run_times_, ElementsAre(start_time + base::Seconds(1)));
EXPECT_THAT(run_order, ElementsAre(task_runner2));
EXPECT_TRUE(task_queue1->IsEmpty());
EXPECT_TRUE(task_queue2->IsEmpty());
}
TEST_F(TimerTest, MoveToNewTaskRunnerRepeating) {
Vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;
scoped_refptr<MainThreadTaskQueue> task_queue1(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
task_queue1->CreateTaskRunner(TaskType::kInternalTest);
TaskObserver task_observer1(task_runner1, &run_order);
task_queue1->AddTaskObserver(&task_observer1);
scoped_refptr<MainThreadTaskQueue> task_queue2(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
task_queue2->CreateTaskRunner(TaskType::kInternalTest);
TaskObserver task_observer2(task_runner2, &run_order);
task_queue2->AddTaskObserver(&task_observer2);
TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);
base::TimeTicks start_time = Now();
timer.StartRepeating(base::Seconds(1), FROM_HERE);
platform_->RunForPeriod(base::Milliseconds(2500));
timer.MoveToNewTaskRunner(task_runner2);
platform_->RunForPeriod(base::Seconds(2));
EXPECT_THAT(run_times_, ElementsAre(start_time + base::Seconds(1),
start_time + base::Seconds(2),
start_time + base::Seconds(3),
start_time + base::Seconds(4)));
EXPECT_THAT(run_order, ElementsAre(task_runner1, task_runner1, task_runner2,
task_runner2));
EXPECT_TRUE(task_queue1->IsEmpty());
EXPECT_FALSE(task_queue2->IsEmpty());
}
// This test checks that when inactive timer is moved to a different task
// runner it isn't activated.
TEST_F(TimerTest, MoveToNewTaskRunnerWithoutTasks) {
scoped_refptr<MainThreadTaskQueue> task_queue1(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
task_queue1->CreateTaskRunner(TaskType::kInternalTest);
scoped_refptr<MainThreadTaskQueue> task_queue2(
platform_->GetMainThreadScheduler()->NewThrottleableTaskQueueForTest(
nullptr));
scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
task_queue2->CreateTaskRunner(TaskType::kInternalTest);
TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);
platform_->RunUntilIdle();
EXPECT_TRUE(!run_times_.size());
EXPECT_TRUE(task_queue1->IsEmpty());
EXPECT_TRUE(task_queue2->IsEmpty());
}
} // namespace
} // namespace blink