You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
905 lines
27 KiB
905 lines
27 KiB
// 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/synchronization/waitable_event.h"
|
|
#include "base/task_scheduler/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 message loops on which each timer should be tested.
|
|
const MessageLoop::Type testing_message_loops[] = {
|
|
MessageLoop::TYPE_DEFAULT, MessageLoop::TYPE_IO,
|
|
#if !defined(OS_IOS) // iOS does not allow direct running of the UI loop.
|
|
MessageLoop::TYPE_UI,
|
|
#endif
|
|
};
|
|
|
|
const int kNumTestingMessageLoops = arraysize(testing_message_loops);
|
|
|
|
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 MessageLoop lives).
|
|
quit_closure_ = Bind(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();
|
|
quit_closure_.Run();
|
|
}
|
|
|
|
RunLoop run_loop_;
|
|
Closure 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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_type);
|
|
|
|
OneShotSelfDeletingTimerTester f;
|
|
f.Start();
|
|
f.WaitAndConfirmTimerFiredAfterDelay();
|
|
}
|
|
|
|
void RunTest_RepeatingTimer(MessageLoop::Type message_loop_type,
|
|
const TimeDelta& delay) {
|
|
MessageLoop loop(message_loop_type);
|
|
|
|
RepeatingTimerTester f(nullptr, delay);
|
|
f.Start();
|
|
f.WaitAndConfirmTimerFiredRepeatedlyAfterDelay();
|
|
}
|
|
|
|
void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type,
|
|
const TimeDelta& delay) {
|
|
MessageLoop loop(message_loop_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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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 < arraysize(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(MessageLoop::Type message_loop_type) {
|
|
MessageLoop loop(message_loop_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 MessageLoop. That way we are sure
|
|
// that timers work properly in all configurations.
|
|
|
|
TEST(TimerTest, OneShotTimers) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_OneShotTimers(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, OneShotTimers_Cancel) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_OneShotTimers_Cancel(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
// If underline timer does not handle properly, we will crash or fail
|
|
// in full page heap environment.
|
|
TEST(TimerTest, OneShotSelfDeletingTimer) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_OneShotSelfDeletingTimer(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, OneShotTimer_CustomTaskRunner) {
|
|
// A MessageLoop is required for the timer events on the other thread to
|
|
// communicate back to the Timer under test.
|
|
MessageLoop loop;
|
|
|
|
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 |loop| before invoking
|
|
// Run() and as such this thread should already be aware that |f| is no longer
|
|
// running.
|
|
EXPECT_TRUE(loop.IsIdleForTesting());
|
|
EXPECT_FALSE(f.IsRunning());
|
|
}
|
|
|
|
TEST(TimerTest, OneShotTimerWithTickClock) {
|
|
scoped_refptr<TestMockTimeTaskRunner> task_runner(
|
|
new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
|
|
MessageLoop message_loop;
|
|
message_loop.SetTaskRunner(task_runner);
|
|
Receiver receiver;
|
|
OneShotTimer timer(task_runner->GetMockTickClock());
|
|
timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
|
|
Bind(&Receiver::OnCalled, Unretained(&receiver)));
|
|
task_runner->FastForwardBy(TimeDelta::FromSeconds(1));
|
|
EXPECT_TRUE(receiver.WasCalled());
|
|
}
|
|
|
|
TEST(TimerTest, RepeatingTimer) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_RepeatingTimer(testing_message_loops[i],
|
|
TimeDelta::FromMilliseconds(10));
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, RepeatingTimer_Cancel) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_RepeatingTimer_Cancel(testing_message_loops[i],
|
|
TimeDelta::FromMilliseconds(10));
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, RepeatingTimerZeroDelay) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_RepeatingTimer(testing_message_loops[i],
|
|
TimeDelta::FromMilliseconds(0));
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, RepeatingTimerZeroDelay_Cancel) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_RepeatingTimer_Cancel(testing_message_loops[i],
|
|
TimeDelta::FromMilliseconds(0));
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, RepeatingTimerWithTickClock) {
|
|
scoped_refptr<TestMockTimeTaskRunner> task_runner(
|
|
new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
|
|
MessageLoop message_loop;
|
|
message_loop.SetTaskRunner(task_runner);
|
|
Receiver receiver;
|
|
const int expected_times_called = 10;
|
|
RepeatingTimer timer(task_runner->GetMockTickClock());
|
|
timer.Start(FROM_HERE, TimeDelta::FromSeconds(1),
|
|
Bind(&Receiver::OnCalled, Unretained(&receiver)));
|
|
task_runner->FastForwardBy(TimeDelta::FromSeconds(expected_times_called));
|
|
timer.Stop();
|
|
EXPECT_EQ(expected_times_called, receiver.TimesCalled());
|
|
}
|
|
|
|
TEST(TimerTest, DelayTimer_NoCall) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_DelayTimer_NoCall(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, DelayTimer_OneCall) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_DelayTimer_OneCall(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
// It's flaky on the buildbot, http://crbug.com/25038.
|
|
TEST(TimerTest, DISABLED_DelayTimer_Reset) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_DelayTimer_Reset(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, DelayTimer_Deleted) {
|
|
for (int i = 0; i < kNumTestingMessageLoops; i++) {
|
|
RunTest_DelayTimer_Deleted(testing_message_loops[i]);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, DelayTimerWithTickClock) {
|
|
scoped_refptr<TestMockTimeTaskRunner> task_runner(
|
|
new TestMockTimeTaskRunner(Time::Now(), TimeTicks::Now()));
|
|
MessageLoop message_loop;
|
|
message_loop.SetTaskRunner(task_runner);
|
|
Receiver receiver;
|
|
DelayTimer timer(FROM_HERE, TimeDelta::FromSeconds(1), &receiver,
|
|
&Receiver::OnCalled, task_runner->GetMockTickClock());
|
|
task_runner->FastForwardBy(TimeDelta::FromMilliseconds(999));
|
|
EXPECT_FALSE(receiver.WasCalled());
|
|
timer.Reset();
|
|
task_runner->FastForwardBy(TimeDelta::FromMilliseconds(999));
|
|
EXPECT_FALSE(receiver.WasCalled());
|
|
timer.Reset();
|
|
task_runner->FastForwardBy(TimeDelta::FromSeconds(1));
|
|
EXPECT_TRUE(receiver.WasCalled());
|
|
}
|
|
|
|
TEST(TimerTest, MessageLoopShutdown) {
|
|
// 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);
|
|
{
|
|
MessageLoop loop;
|
|
a.Start();
|
|
b.Start();
|
|
} // MessageLoop 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),
|
|
base::Bind(&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, MessageLoopShutdownSelfOwningTimer) {
|
|
// This test verifies that shutdown of the message loop 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.
|
|
|
|
MessageLoop loop;
|
|
scoped_refptr<OneShotSelfOwningTimerTester> tester =
|
|
new OneShotSelfOwningTimerTester();
|
|
|
|
std::move(tester)->StartTimer();
|
|
// |Timer::user_task_| owns sole reference to |tester|.
|
|
|
|
// MessageLoop destructs by falling out of scope. SHOULD NOT CRASH.
|
|
}
|
|
|
|
void TimerTestCallback() {
|
|
}
|
|
|
|
TEST(TimerTest, NonRepeatIsRunning) {
|
|
{
|
|
MessageLoop loop;
|
|
Timer timer(false, false);
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
timer.Stop();
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
EXPECT_TRUE(timer.user_task().is_null());
|
|
}
|
|
|
|
{
|
|
Timer timer(true, false);
|
|
MessageLoop loop;
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&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, NonRepeatMessageLoopDeath) {
|
|
Timer timer(false, false);
|
|
{
|
|
MessageLoop loop;
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
}
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
EXPECT_TRUE(timer.user_task().is_null());
|
|
}
|
|
|
|
TEST(TimerTest, RetainRepeatIsRunning) {
|
|
MessageLoop loop;
|
|
Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
|
|
true);
|
|
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) {
|
|
MessageLoop loop;
|
|
Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
|
|
false);
|
|
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();
|
|
MessageLoop loop;
|
|
Timer timer(false, false);
|
|
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
|
|
Bind(&SetCallbackHappened1));
|
|
timer.Stop();
|
|
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(40),
|
|
Bind(&SetCallbackHappened2));
|
|
RunLoop().Run();
|
|
EXPECT_FALSE(g_callback_happened1);
|
|
EXPECT_TRUE(g_callback_happened2);
|
|
}
|
|
}
|
|
|
|
TEST(TimerTest, ContinuationReset) {
|
|
{
|
|
ClearAllCallbackHappened();
|
|
MessageLoop loop;
|
|
Timer timer(false, false);
|
|
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
|
|
Bind(&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);
|
|
}
|
|
}
|
|
|
|
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().
|
|
Closure TaskWithSignal(const Closure& task) {
|
|
return Bind(&TimerSequenceTest::RunTaskAndSignal, Unretained(this), task);
|
|
}
|
|
|
|
// Create the timer.
|
|
void CreateTimer() { timer_.reset(new OneShotTimer); }
|
|
|
|
// Schedule an event on the timer.
|
|
void StartTimer(TimeDelta delay, const Closure& task) {
|
|
timer_->Start(FROM_HERE, delay, 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(const Closure& task) {
|
|
task.Run();
|
|
Signal();
|
|
}
|
|
|
|
base::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),
|
|
Bind(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
|
|
// MessageLoop 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),
|
|
Bind(&TimerSequenceTest::Signal, Unretained(this))));
|
|
Wait();
|
|
|
|
// Timer must be destroyed on pool thread, too.
|
|
task_runner->PostTask(
|
|
FROM_HERE,
|
|
TaskWithSignal(Bind(&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(Bind(&TimerSequenceTest::CreateTimer, Unretained(this))));
|
|
Wait();
|
|
|
|
// And tell it to execute on a different sequence (#2).
|
|
task_runner1->PostTask(
|
|
FROM_HERE, TaskWithSignal(Bind(&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()));
|
|
|
|
// Abandon task - must be called from scheduling sequence (#1).
|
|
task_runner1->PostTask(
|
|
FROM_HERE,
|
|
TaskWithSignal(Bind(&TimerSequenceTest::AbandonTask, Unretained(this))));
|
|
Wait();
|
|
|
|
// Timer must be destroyed on the sequence it was scheduled from (#1).
|
|
task_runner1->PostTask(
|
|
FROM_HERE,
|
|
TaskWithSignal(Bind(&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(Bind(&TimerSequenceTest::CreateTimer, Unretained(this))));
|
|
Wait();
|
|
|
|
// And tell it to execute on a different sequence (#2).
|
|
task_runner1->PostTask(
|
|
FROM_HERE, TaskWithSignal(Bind(&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(Bind(&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(Bind(&TimerSequenceTest::DeleteTimer, Unretained(this))));
|
|
Wait();
|
|
}
|
|
|
|
} // namespace base
|