blob: b137a66578080b2becbd5034636436ed7cc927a7 [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 "base/timer/timer.h"
#include <stddef.h>
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/post_task.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
// The main thread types on which each timer should be tested.
const test::ScopedTaskEnvironment::MainThreadType testing_main_threads[] = {
test::ScopedTaskEnvironment::MainThreadType::DEFAULT,
test::ScopedTaskEnvironment::MainThreadType::IO,
#if !defined(OS_IOS) // iOS does not allow direct running of the UI loop.
test::ScopedTaskEnvironment::MainThreadType::UI,
#endif
};
class Receiver {
public:
Receiver() : count_(0) {}
void OnCalled() { count_++; }
bool WasCalled() { return count_ > 0; }
int TimesCalled() { return count_; }
private:
int count_;
};
// A basic helper class that can start a one-shot timer and signal a
// WaitableEvent when this timer fires.
class OneShotTimerTesterBase {
public:
// |did_run|, if provided, will be signaled when Run() fires.
explicit OneShotTimerTesterBase(
WaitableEvent* did_run = nullptr,
const TimeDelta& delay = TimeDelta::FromMilliseconds(10))
: did_run_(did_run), delay_(delay) {}
virtual ~OneShotTimerTesterBase() = default;
void Start() {
started_time_ = TimeTicks::Now();
timer_->Start(FROM_HERE, delay_, this, &OneShotTimerTesterBase::Run);
}
bool IsRunning() { return timer_->IsRunning(); }
TimeTicks started_time() const { return started_time_; }
TimeDelta delay() const { return delay_; }
protected:
virtual void Run() {
if (did_run_) {
EXPECT_FALSE(did_run_->IsSignaled());
did_run_->Signal();
}
}
std::unique_ptr<OneShotTimer> timer_ = std::make_unique<OneShotTimer>();
private:
WaitableEvent* const did_run_;
const TimeDelta delay_;
TimeTicks started_time_;
DISALLOW_COPY_AND_ASSIGN(OneShotTimerTesterBase);
};
// Extends functionality of OneShotTimerTesterBase with the abilities to wait
// until the timer fires and to change task runner for the timer.
class OneShotTimerTester : public OneShotTimerTesterBase {
public:
// |did_run|, if provided, will be signaled when Run() fires.
explicit OneShotTimerTester(
WaitableEvent* did_run = nullptr,
const TimeDelta& delay = TimeDelta::FromMilliseconds(10))
: OneShotTimerTesterBase(did_run, delay),
quit_closure_(run_loop_.QuitClosure()) {}
~OneShotTimerTester() override = default;
void SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
timer_->SetTaskRunner(std::move(task_runner));
// Run() will be invoked on |task_runner| but |run_loop_|'s QuitClosure
// needs to run on this thread (where the task environment lives).
quit_closure_ = BindOnce(IgnoreResult(&SequencedTaskRunner::PostTask),
SequencedTaskRunnerHandle::Get(), FROM_HERE,
run_loop_.QuitClosure());
}
// Blocks until Run() executes and confirms that Run() didn't fire before
// |delay_| expired.
void WaitAndConfirmTimerFiredAfterDelay() {
run_loop_.Run();
EXPECT_NE(TimeTicks(), started_time());
EXPECT_GE(TimeTicks::Now() - started_time(), delay());
}
protected:
// Overridable method to do things on Run() before signaling events/closures
// managed by this helper.
virtual void OnRun() {}
private:
void Run() override {
OnRun();
OneShotTimerTesterBase::Run();
std::move(quit_closure_).Run();
}
RunLoop run_loop_;
OnceClosure quit_closure_;
DISALLOW_COPY_AND_ASSIGN(OneShotTimerTester);
};
class OneShotSelfDeletingTimerTester : public OneShotTimerTester {
protected:
void OnRun() override { timer_.reset(); }
};
constexpr int kNumRepeats = 10;
class RepeatingTimerTester {
public:
explicit RepeatingTimerTester(WaitableEvent* did_run, const TimeDelta& delay)
: counter_(kNumRepeats),
quit_closure_(run_loop_.QuitClosure()),
did_run_(did_run),
delay_(delay) {}
void Start() {
started_time_ = TimeTicks::Now();
timer_.Start(FROM_HERE, delay_, this, &RepeatingTimerTester::Run);
}
void WaitAndConfirmTimerFiredRepeatedlyAfterDelay() {
run_loop_.Run();
EXPECT_NE(TimeTicks(), started_time_);
EXPECT_GE(TimeTicks::Now() - started_time_, kNumRepeats * delay_);
}
private:
void Run() {
if (--counter_ == 0) {
if (did_run_) {
EXPECT_FALSE(did_run_->IsSignaled());
did_run_->Signal();
}
timer_.Stop();
quit_closure_.Run();
}
}
RepeatingTimer timer_;
int counter_;
RunLoop run_loop_;
Closure quit_closure_;
WaitableEvent* const did_run_;
const TimeDelta delay_;
TimeTicks started_time_;
DISALLOW_COPY_AND_ASSIGN(RepeatingTimerTester);
};
// Basic test with same setup as RunTest_OneShotTimers_Cancel below to confirm
// that |did_run_a| would be signaled in that test if it wasn't for the
// deletion.
void RunTest_OneShotTimers(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
OneShotTimerTester a(&did_run_a);
a.Start();
OneShotTimerTester b;
b.Start();
b.WaitAndConfirmTimerFiredAfterDelay();
EXPECT_TRUE(did_run_a.IsSignaled());
}
void RunTest_OneShotTimers_Cancel(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
OneShotTimerTester* a = new OneShotTimerTester(&did_run_a);
// This should run before the timer expires.
SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
OneShotTimerTester b;
b.Start();
b.WaitAndConfirmTimerFiredAfterDelay();
EXPECT_FALSE(did_run_a.IsSignaled());
}
void RunTest_OneShotSelfDeletingTimer(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
OneShotSelfDeletingTimerTester f;
f.Start();
f.WaitAndConfirmTimerFiredAfterDelay();
}
void RunTest_RepeatingTimer(
test::ScopedTaskEnvironment::MainThreadType main_thread_type,
const TimeDelta& delay) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
RepeatingTimerTester f(nullptr, delay);
f.Start();
f.WaitAndConfirmTimerFiredRepeatedlyAfterDelay();
}
void RunTest_RepeatingTimer_Cancel(
test::ScopedTaskEnvironment::MainThreadType main_thread_type,
const TimeDelta& delay) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
WaitableEvent did_run_a(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a, delay);
// This should run before the timer expires.
SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
// Now start the timer.
a->Start();
RepeatingTimerTester b(nullptr, delay);
b.Start();
b.WaitAndConfirmTimerFiredRepeatedlyAfterDelay();
// |a| should not have fired despite |b| starting after it on the same
// sequence and being complete by now.
EXPECT_FALSE(did_run_a.IsSignaled());
}
class DelayTimerTarget {
public:
bool signaled() const { return signaled_; }
void Signal() {
ASSERT_FALSE(signaled_);
signaled_ = true;
}
private:
bool signaled_ = false;
};
void RunTest_DelayTimer_NoCall(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
&DelayTimerTarget::Signal);
OneShotTimerTester tester;
tester.Start();
tester.WaitAndConfirmTimerFiredAfterDelay();
ASSERT_FALSE(target.signaled());
}
void RunTest_DelayTimer_OneCall(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
DelayTimerTarget target;
DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(1), &target,
&DelayTimerTarget::Signal);
timer.Reset();
OneShotTimerTester tester(nullptr, TimeDelta::FromMilliseconds(100));
tester.Start();
tester.WaitAndConfirmTimerFiredAfterDelay();
ASSERT_TRUE(target.signaled());
}
struct ResetHelper {
ResetHelper(DelayTimer* timer, DelayTimerTarget* target)
: timer_(timer), target_(target) {}
void Reset() {
ASSERT_FALSE(target_->signaled());
timer_->Reset();
}
private:
DelayTimer* const timer_;
DelayTimerTarget* const target_;
};
void RunTest_DelayTimer_Reset(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
// If Delay is never called, the timer shouldn't go off.
DelayTimerTarget target;
DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
&DelayTimerTarget::Signal);
timer.Reset();
ResetHelper reset_helper(&timer, &target);
OneShotTimer timers[20];
for (size_t i = 0; i < base::size(timers); ++i) {
timers[i].Start(FROM_HERE, TimeDelta::FromMilliseconds(i * 10),
&reset_helper, &ResetHelper::Reset);
}
OneShotTimerTester tester(nullptr, TimeDelta::FromMilliseconds(300));
tester.Start();
tester.WaitAndConfirmTimerFiredAfterDelay();
ASSERT_TRUE(target.signaled());
}
class DelayTimerFatalTarget {
public:
void Signal() {
ASSERT_TRUE(false);
}
};
void RunTest_DelayTimer_Deleted(
test::ScopedTaskEnvironment::MainThreadType main_thread_type) {
test::ScopedTaskEnvironment scoped_task_environment(main_thread_type);
DelayTimerFatalTarget target;
{
DelayTimer timer(FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
&DelayTimerFatalTarget::Signal);
timer.Reset();
}
// When the timer is deleted, the DelayTimerFatalTarget should never be
// called.
PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
}
} // namespace
//-----------------------------------------------------------------------------
// Each test is run against each type of main thread. That way we are sure
// that timers work properly in all configurations.
class TimerTestWithThreadType
: public testing::TestWithParam<
test::ScopedTaskEnvironment::MainThreadType> {};
TEST_P(TimerTestWithThreadType, OneShotTimers) {
RunTest_OneShotTimers(GetParam());
}
TEST_P(TimerTestWithThreadType, OneShotTimers_Cancel) {
RunTest_OneShotTimers_Cancel(GetParam());
}
// If underline timer does not handle properly, we will crash or fail
// in full page heap environment.
TEST_P(TimerTestWithThreadType, OneShotSelfDeletingTimer) {
RunTest_OneShotSelfDeletingTimer(GetParam());
}
TEST(TimerTest, OneShotTimer_CustomTaskRunner) {
// A task environment is required for the timer events on the other thread to
// communicate back to the Timer under test.
test::ScopedTaskEnvironment scoped_task_environment;
Thread other_thread("OneShotTimer_CustomTaskRunner");
other_thread.Start();
WaitableEvent did_run(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
OneShotTimerTester f(&did_run);
f.SetTaskRunner(other_thread.task_runner());
f.Start();
EXPECT_TRUE(f.IsRunning() || did_run.IsSignaled());
f.WaitAndConfirmTimerFiredAfterDelay();
EXPECT_TRUE(did_run.IsSignaled());
// |f| should already have communicated back to this main thread before
// invoking Run() and as such this thread should already be aware that |f| is
// no longer running.
EXPECT_FALSE(scoped_task_environment.MainThreadHasPendingTask());
EXPECT_FALSE(f.IsRunning());
}
TEST(TimerTest, OneShotTimerWithTickClock) {
test::ScopedTaskEnvironment scoped_task_environment(
test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
Receiver receiver;
OneShotTimer timer(scoped_task_environment.GetMockTickClock());
timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
BindOnce(&Receiver::OnCalled, Unretained(&receiver)));
scoped_task_environment.FastForwardBy(TimeDelta::FromSeconds(1));
EXPECT_TRUE(receiver.WasCalled());
}
TEST_P(TimerTestWithThreadType, RepeatingTimer) {
RunTest_RepeatingTimer(GetParam(), TimeDelta::FromMilliseconds(10));
}
TEST_P(TimerTestWithThreadType, RepeatingTimer_Cancel) {
RunTest_RepeatingTimer_Cancel(GetParam(), TimeDelta::FromMilliseconds(10));
}
TEST_P(TimerTestWithThreadType, RepeatingTimerZeroDelay) {
RunTest_RepeatingTimer(GetParam(), TimeDelta::FromMilliseconds(0));
}
TEST_P(TimerTestWithThreadType, RepeatingTimerZeroDelay_Cancel) {
RunTest_RepeatingTimer_Cancel(GetParam(), TimeDelta::FromMilliseconds(0));
}
TEST(TimerTest, RepeatingTimerWithTickClock) {
test::ScopedTaskEnvironment scoped_task_environment(
test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
Receiver receiver;
const int expected_times_called = 10;
RepeatingTimer timer(scoped_task_environment.GetMockTickClock());
timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
BindRepeating(&Receiver::OnCalled, Unretained(&receiver)));
scoped_task_environment.FastForwardBy(
TimeDelta::FromSeconds(expected_times_called));
timer.Stop();
EXPECT_EQ(expected_times_called, receiver.TimesCalled());
}
TEST_P(TimerTestWithThreadType, DelayTimer_NoCall) {
RunTest_DelayTimer_NoCall(GetParam());
}
TEST_P(TimerTestWithThreadType, DelayTimer_OneCall) {
RunTest_DelayTimer_OneCall(GetParam());
}
// It's flaky on the buildbot, http://crbug.com/25038.
TEST_P(TimerTestWithThreadType, DISABLED_DelayTimer_Reset) {
RunTest_DelayTimer_Reset(GetParam());
}
TEST_P(TimerTestWithThreadType, DelayTimer_Deleted) {
RunTest_DelayTimer_Deleted(GetParam());
}
TEST(TimerTest, DelayTimerWithTickClock) {
test::ScopedTaskEnvironment scoped_task_environment(
test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME);
Receiver receiver;
DelayTimer timer(FROM_HERE, TimeDelta::FromSeconds(1), &receiver,
&Receiver::OnCalled,
scoped_task_environment.GetMockTickClock());
scoped_task_environment.FastForwardBy(TimeDelta::FromMilliseconds(999));
EXPECT_FALSE(receiver.WasCalled());
timer.Reset();
scoped_task_environment.FastForwardBy(TimeDelta::FromMilliseconds(999));
EXPECT_FALSE(receiver.WasCalled());
timer.Reset();
scoped_task_environment.FastForwardBy(TimeDelta::FromSeconds(1));
EXPECT_TRUE(receiver.WasCalled());
}
TEST(TimerTest, TaskEnvironmentShutdown) {
// This test is designed to verify that shutdown of the
// message loop does not cause crashes if there were pending
// timers not yet fired. It may only trigger exceptions
// if debug heap checking is enabled.
WaitableEvent did_run(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
{
OneShotTimerTesterBase a(&did_run);
OneShotTimerTesterBase b(&did_run);
OneShotTimerTesterBase c(&did_run);
OneShotTimerTesterBase d(&did_run);
{
test::ScopedTaskEnvironment scoped_task_environment;
a.Start();
b.Start();
} // Task environment destructs by falling out of scope.
} // OneShotTimers destruct. SHOULD NOT CRASH, of course.
EXPECT_FALSE(did_run.IsSignaled());
}
// Ref counted class which owns a Timer. The class passes a reference to itself
// via the |user_task| parameter in Timer::Start(). |Timer::user_task_| might
// end up holding the last reference to the class.
class OneShotSelfOwningTimerTester
: public RefCounted<OneShotSelfOwningTimerTester> {
public:
OneShotSelfOwningTimerTester() = default;
void StartTimer() {
// Start timer with long delay in order to test the timer getting destroyed
// while a timer task is still pending.
timer_.Start(FROM_HERE, TimeDelta::FromDays(1),
BindOnce(&OneShotSelfOwningTimerTester::Run, this));
}
private:
friend class RefCounted<OneShotSelfOwningTimerTester>;
~OneShotSelfOwningTimerTester() = default;
void Run() {
ADD_FAILURE() << "Timer unexpectedly fired.";
}
OneShotTimer timer_;
DISALLOW_COPY_AND_ASSIGN(OneShotSelfOwningTimerTester);
};
TEST(TimerTest, TaskEnvironmentShutdownSelfOwningTimer) {
// This test verifies that shutdown of the task environment does not cause
// crashes if there is a pending timer not yet fired and |Timer::user_task_|
// owns the timer. The test may only trigger exceptions if debug heap checking
// is enabled.
test::ScopedTaskEnvironment scoped_task_environment;
scoped_refptr<OneShotSelfOwningTimerTester> tester =
new OneShotSelfOwningTimerTester();
std::move(tester)->StartTimer();
// |Timer::user_task_| owns sole reference to |tester|.
// Task environment destructs by falling out of scope. SHOULD NOT CRASH.
}
void TimerTestCallback() {
}
TEST(TimerTest, NonRepeatIsRunning) {
{
test::ScopedTaskEnvironment scoped_task_environment;
OneShotTimer timer;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
BindOnce(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
}
{
RetainingOneShotTimer timer;
test::ScopedTaskEnvironment scoped_task_environment;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
BindRepeating(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
ASSERT_FALSE(timer.user_task().is_null());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
}
}
TEST(TimerTest, NonRepeatTaskEnvironmentDeath) {
OneShotTimer timer;
{
test::ScopedTaskEnvironment scoped_task_environment;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
BindOnce(&TimerTestCallback));
EXPECT_TRUE(timer.IsRunning());
}
EXPECT_FALSE(timer.IsRunning());
}
TEST(TimerTest, RetainRepeatIsRunning) {
test::ScopedTaskEnvironment scoped_task_environment;
RepeatingTimer timer(FROM_HERE, TimeDelta::FromDays(1),
BindRepeating(&TimerTestCallback));
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
}
TEST(TimerTest, RetainNonRepeatIsRunning) {
test::ScopedTaskEnvironment scoped_task_environment;
RetainingOneShotTimer timer(FROM_HERE, TimeDelta::FromDays(1),
BindRepeating(&TimerTestCallback));
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
timer.Reset();
EXPECT_TRUE(timer.IsRunning());
}
//-----------------------------------------------------------------------------
namespace {
bool g_callback_happened1 = false;
bool g_callback_happened2 = false;
void ClearAllCallbackHappened() {
g_callback_happened1 = false;
g_callback_happened2 = false;
}
void SetCallbackHappened1() {
g_callback_happened1 = true;
RunLoop::QuitCurrentWhenIdleDeprecated();
}
void SetCallbackHappened2() {
g_callback_happened2 = true;
RunLoop::QuitCurrentWhenIdleDeprecated();
}
} // namespace
TEST(TimerTest, ContinuationStopStart) {
{
ClearAllCallbackHappened();
test::ScopedTaskEnvironment scoped_task_environment;
OneShotTimer timer;
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
BindOnce(&SetCallbackHappened1));
timer.Stop();
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(40),
BindOnce(&SetCallbackHappened2));
RunLoop().Run();
EXPECT_FALSE(g_callback_happened1);
EXPECT_TRUE(g_callback_happened2);
}
}
TEST(TimerTest, ContinuationReset) {
{
ClearAllCallbackHappened();
test::ScopedTaskEnvironment scoped_task_environment;
OneShotTimer timer;
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
BindOnce(&SetCallbackHappened1));
timer.Reset();
// // Since Reset happened before task ran, the user_task must not be
// cleared: ASSERT_FALSE(timer.user_task().is_null());
RunLoop().Run();
EXPECT_TRUE(g_callback_happened1);
}
}
INSTANTIATE_TEST_CASE_P(,
TimerTestWithThreadType,
testing::ValuesIn(testing_main_threads));
namespace {
// Fixture for tests requiring ScopedTaskEnvironment. Includes a WaitableEvent
// so that cases may Wait() on one thread and Signal() (explicitly, or
// implicitly via helper methods) on another.
class TimerSequenceTest : public testing::Test {
public:
TimerSequenceTest()
: event_(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED) {}
// Block until Signal() is called on another thread.
void Wait() { event_.Wait(); }
void Signal() { event_.Signal(); }
// Helper to augment a task with a subsequent call to Signal().
OnceClosure TaskWithSignal(OnceClosure task) {
return BindOnce(&TimerSequenceTest::RunTaskAndSignal, Unretained(this),
std::move(task));
}
// Create the timer.
void CreateTimer() { timer_.reset(new OneShotTimer); }
// Schedule an event on the timer.
void StartTimer(TimeDelta delay, OnceClosure task) {
timer_->Start(FROM_HERE, delay, std::move(task));
}
void SetTaskRunnerForTimer(scoped_refptr<SequencedTaskRunner> task_runner) {
timer_->SetTaskRunner(std::move(task_runner));
}
// Tell the timer to abandon the task.
void AbandonTask() {
EXPECT_TRUE(timer_->IsRunning());
// Reset() to call Timer::AbandonScheduledTask()
timer_->Reset();
EXPECT_TRUE(timer_->IsRunning());
timer_->Stop();
EXPECT_FALSE(timer_->IsRunning());
}
static void VerifyAffinity(const SequencedTaskRunner* task_runner) {
EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
}
// Delete the timer.
void DeleteTimer() { timer_.reset(); }
private:
void RunTaskAndSignal(OnceClosure task) {
std::move(task).Run();
Signal();
}
test::ScopedTaskEnvironment scoped_task_environment_;
WaitableEvent event_;
std::unique_ptr<OneShotTimer> timer_;
DISALLOW_COPY_AND_ASSIGN(TimerSequenceTest);
};
} // namespace
TEST_F(TimerSequenceTest, OneShotTimerTaskOnPoolSequence) {
scoped_refptr<SequencedTaskRunner> task_runner =
base::CreateSequencedTaskRunnerWithTraits({});
base::RunLoop run_loop_;
// Timer is created on this thread.
CreateTimer();
// Task will execute on a pool thread.
SetTaskRunnerForTimer(task_runner);
StartTimer(TimeDelta::FromMilliseconds(1),
BindOnce(IgnoreResult(&SequencedTaskRunner::PostTask),
SequencedTaskRunnerHandle::Get(), FROM_HERE,
run_loop_.QuitClosure()));
// Spin the loop so that the delayed task fires on it, which will forward it
// to |task_runner|. And since the Timer's task is one that posts back to this
// thread to quit, we finally unblock.
run_loop_.Run();
// Timer will be destroyed on this thread.
DeleteTimer();
}
TEST_F(TimerSequenceTest, OneShotTimerUsedOnPoolSequence) {
scoped_refptr<SequencedTaskRunner> task_runner =
base::CreateSequencedTaskRunnerWithTraits({});
// Timer is created on this thread.
CreateTimer();
// Task will be scheduled from a pool thread.
task_runner->PostTask(
FROM_HERE,
BindOnce(&TimerSequenceTest::StartTimer, Unretained(this),
TimeDelta::FromMilliseconds(1),
BindOnce(&TimerSequenceTest::Signal, Unretained(this))));
Wait();
// Timer must be destroyed on pool thread, too.
task_runner->PostTask(FROM_HERE,
TaskWithSignal(BindOnce(&TimerSequenceTest::DeleteTimer,
Unretained(this))));
Wait();
}
TEST_F(TimerSequenceTest, OneShotTimerTwoSequencesAbandonTask) {
scoped_refptr<SequencedTaskRunner> task_runner1 =
base::CreateSequencedTaskRunnerWithTraits({});
scoped_refptr<SequencedTaskRunner> task_runner2 =
base::CreateSequencedTaskRunnerWithTraits({});
// Create timer on sequence #1.
task_runner1->PostTask(
FROM_HERE, TaskWithSignal(BindOnce(&TimerSequenceTest::CreateTimer,
Unretained(this))));
Wait();
// And tell it to execute on a different sequence (#2).
task_runner1->PostTask(
FROM_HERE,
TaskWithSignal(BindOnce(&TimerSequenceTest::SetTaskRunnerForTimer,
Unretained(this), task_runner2)));
Wait();
// Task will be scheduled from sequence #1.
task_runner1->PostTask(
FROM_HERE, BindOnce(&TimerSequenceTest::StartTimer, Unretained(this),
TimeDelta::FromHours(1), DoNothing().Once()));
// Abandon task - must be called from scheduling sequence (#1).
task_runner1->PostTask(
FROM_HERE, TaskWithSignal(BindOnce(&TimerSequenceTest::AbandonTask,
Unretained(this))));
Wait();
// Timer must be destroyed on the sequence it was scheduled from (#1).
task_runner1->PostTask(
FROM_HERE, TaskWithSignal(BindOnce(&TimerSequenceTest::DeleteTimer,
Unretained(this))));
Wait();
}
TEST_F(TimerSequenceTest, OneShotTimerUsedAndTaskedOnDifferentSequences) {
scoped_refptr<SequencedTaskRunner> task_runner1 =
base::CreateSequencedTaskRunnerWithTraits({});
scoped_refptr<SequencedTaskRunner> task_runner2 =
base::CreateSequencedTaskRunnerWithTraits({});
// Create timer on sequence #1.
task_runner1->PostTask(
FROM_HERE, TaskWithSignal(BindOnce(&TimerSequenceTest::CreateTimer,
Unretained(this))));
Wait();
// And tell it to execute on a different sequence (#2).
task_runner1->PostTask(
FROM_HERE,
TaskWithSignal(BindOnce(&TimerSequenceTest::SetTaskRunnerForTimer,
Unretained(this), task_runner2)));
Wait();
// Task will be scheduled from sequence #1.
task_runner1->PostTask(
FROM_HERE,
BindOnce(&TimerSequenceTest::StartTimer, Unretained(this),
TimeDelta::FromMilliseconds(1),
TaskWithSignal(BindOnce(&TimerSequenceTest::VerifyAffinity,
Unretained(task_runner2.get())))));
Wait();
// Timer must be destroyed on the sequence it was scheduled from (#1).
task_runner1->PostTask(
FROM_HERE, TaskWithSignal(BindOnce(&TimerSequenceTest::DeleteTimer,
Unretained(this))));
Wait();
}
} // namespace base