| Index: base/timer/timer_unittest.cc
|
| diff --git a/base/timer/timer_unittest.cc b/base/timer/timer_unittest.cc
|
| index 35e4315ea46368b392fd7e861398ba0440eb8a79..1172110ce1faaf062a01e92c1792c108f734c414 100644
|
| --- a/base/timer/timer_unittest.cc
|
| +++ b/base/timer/timer_unittest.cc
|
| @@ -2,14 +2,19 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include "base/bind_helpers.h"
|
| +#include "base/callback.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/message_loop/message_loop.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/test/sequenced_worker_pool_owner.h"
|
| #include "base/test/test_simple_task_runner.h"
|
| #include "base/timer/timer.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +using base::DoNothing;
|
| +using base::SequencedTaskRunner;
|
| using base::TimeDelta;
|
| -using base::SingleThreadTaskRunner;
|
|
|
| namespace {
|
|
|
| @@ -37,7 +42,7 @@ class OneShotTimerTester {
|
| &OneShotTimerTester::Run);
|
| }
|
|
|
| - void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner) {
|
| + void SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
|
| quit_message_loop_ = false;
|
| timer_.SetTaskRunner(task_runner);
|
| }
|
| @@ -528,4 +533,201 @@ TEST(TimerTest, ContinuationReset) {
|
| }
|
| }
|
|
|
| +const size_t kNumWorkerThreads = 3;
|
| +
|
| +// Fixture for tests requiring a worker pool. 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_(false /* manual_reset */, false /* initially_signaled */) {}
|
| +
|
| + void SetUp() override { ResetPools(); }
|
| +
|
| + void TearDown() override {
|
| + pool1()->Shutdown();
|
| + pool2()->Shutdown();
|
| + }
|
| +
|
| + // 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().
|
| + base::Closure TaskWithSignal(const base::Closure& task) {
|
| + return base::Bind(&TimerSequenceTest::RunTaskAndSignal,
|
| + base::Unretained(this), task);
|
| + }
|
| +
|
| + // Create the timer.
|
| + void CreateTimer() { timer_.reset(new base::OneShotTimer); }
|
| +
|
| + // Schedule an event on the timer.
|
| + void StartTimer(TimeDelta delay, const base::Closure& task) {
|
| + timer_->Start(FROM_HERE, delay, task);
|
| + }
|
| +
|
| + void SetTaskRunnerForTimer(SequencedTaskRunner* task_runner) {
|
| + timer_->SetTaskRunner(task_runner);
|
| + }
|
| +
|
| + // Tell the timer to abandon the task.
|
| + void AbandonTask() {
|
| + ASSERT_TRUE(timer_->IsRunning());
|
| + // Reset() to call Timer::AbandonScheduledTask()
|
| + timer_->Reset();
|
| + ASSERT_TRUE(timer_->IsRunning());
|
| + timer_->Stop();
|
| + ASSERT_FALSE(timer_->IsRunning());
|
| + }
|
| +
|
| + static void VerifyAffinity(SequencedTaskRunner* task_runner) {
|
| + ASSERT_TRUE(task_runner->RunsTasksOnCurrentThread());
|
| + }
|
| +
|
| + // Delete the timer.
|
| + void DeleteTimer() { timer_.reset(); }
|
| +
|
| + protected:
|
| + const scoped_refptr<base::SequencedWorkerPool>& pool1() {
|
| + return pool1_owner_->pool();
|
| + }
|
| + const scoped_refptr<base::SequencedWorkerPool>& pool2() {
|
| + return pool2_owner_->pool();
|
| + }
|
| +
|
| + // Destroys the SequencedWorkerPool instance, blocking until it is fully shut
|
| + // down, and creates a new instance.
|
| + void ResetPools() {
|
| + pool1_owner_.reset(
|
| + new base::SequencedWorkerPoolOwner(kNumWorkerThreads, "test1"));
|
| + pool2_owner_.reset(
|
| + new base::SequencedWorkerPoolOwner(kNumWorkerThreads, "test2"));
|
| + }
|
| +
|
| + private:
|
| + void RunTaskAndSignal(const base::Closure& task) {
|
| + task.Run();
|
| + Signal();
|
| + }
|
| +
|
| + base::WaitableEvent event_;
|
| +
|
| + base::MessageLoop message_loop_;
|
| + scoped_ptr<base::SequencedWorkerPoolOwner> pool1_owner_;
|
| + scoped_ptr<base::SequencedWorkerPoolOwner> pool2_owner_;
|
| +
|
| + scoped_ptr<base::OneShotTimer> timer_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TimerSequenceTest);
|
| +};
|
| +
|
| +TEST_F(TimerSequenceTest, OneShotTimerTaskOnPoolThread) {
|
| + scoped_refptr<SequencedTaskRunner> task_runner =
|
| + pool1()->GetSequencedTaskRunner(pool1()->GetSequenceToken());
|
| +
|
| + // Timer is created on this thread.
|
| + CreateTimer();
|
| +
|
| + // Task will execute on a pool thread.
|
| + SetTaskRunnerForTimer(task_runner.get());
|
| + StartTimer(TimeDelta::FromMilliseconds(1),
|
| + base::Bind(&TimerSequenceTest::Signal, base::Unretained(this)));
|
| + Wait();
|
| +
|
| + // Timer will be destroyed on this thread.
|
| + DeleteTimer();
|
| +}
|
| +
|
| +TEST_F(TimerSequenceTest, OneShotTimerUsedOnPoolThread) {
|
| + scoped_refptr<SequencedTaskRunner> task_runner =
|
| + pool1()->GetSequencedTaskRunner(pool1()->GetSequenceToken());
|
| +
|
| + // Timer is created on this thread.
|
| + CreateTimer();
|
| +
|
| + // Task will be scheduled from a pool thread.
|
| + task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &TimerSequenceTest::StartTimer, base::Unretained(this),
|
| + TimeDelta::FromMilliseconds(1),
|
| + base::Bind(&TimerSequenceTest::Signal, base::Unretained(this))));
|
| + Wait();
|
| +
|
| + // Timer must be destroyed on pool thread, too.
|
| + task_runner->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::DeleteTimer,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +}
|
| +
|
| +TEST_F(TimerSequenceTest, OneShotTimerTwoPoolsAbandonTask) {
|
| + scoped_refptr<SequencedTaskRunner> task_runner1 =
|
| + pool1()->GetSequencedTaskRunner(pool1()->GetSequenceToken());
|
| + scoped_refptr<SequencedTaskRunner> task_runner2 =
|
| + pool2()->GetSequencedTaskRunner(pool2()->GetSequenceToken());
|
| +
|
| + // Create timer on pool #1.
|
| + task_runner1->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::CreateTimer,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +
|
| + // Task will execute on a different pool (#2).
|
| + SetTaskRunnerForTimer(task_runner2.get());
|
| +
|
| + // Task will be scheduled from pool #1.
|
| + task_runner1->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&TimerSequenceTest::StartTimer, base::Unretained(this),
|
| + TimeDelta::FromHours(1), base::Bind(&DoNothing)));
|
| +
|
| + // Abandon task - must be called from scheduling pool (#1).
|
| + task_runner1->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::AbandonTask,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +
|
| + // Timer must be destroyed on the pool it was scheduled from (#1).
|
| + task_runner1->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::DeleteTimer,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +}
|
| +
|
| +TEST_F(TimerSequenceTest, OneShotTimerUsedAndTaskedOnDifferentPools) {
|
| + scoped_refptr<SequencedTaskRunner> task_runner1 =
|
| + pool1()->GetSequencedTaskRunner(pool1()->GetSequenceToken());
|
| + scoped_refptr<SequencedTaskRunner> task_runner2 =
|
| + pool2()->GetSequencedTaskRunner(pool2()->GetSequenceToken());
|
| +
|
| + // Create timer on pool #1.
|
| + task_runner1->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::CreateTimer,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +
|
| + // Task will execute on a different pool (#2).
|
| + SetTaskRunnerForTimer(task_runner2.get());
|
| +
|
| + // Task will be scheduled from pool #1.
|
| + task_runner1->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&TimerSequenceTest::StartTimer, base::Unretained(this),
|
| + TimeDelta::FromMilliseconds(1),
|
| + TaskWithSignal(base::Bind(&TimerSequenceTest::VerifyAffinity,
|
| + task_runner2))));
|
| +
|
| + Wait();
|
| +
|
| + // Timer must be destroyed on the pool it was scheduled from (#1).
|
| + task_runner1->PostTask(
|
| + FROM_HERE, TaskWithSignal(base::Bind(&TimerSequenceTest::DeleteTimer,
|
| + base::Unretained(this))));
|
| + Wait();
|
| +}
|
| +
|
| } // namespace
|
|
|