diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-29 01:33:03 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-29 01:33:03 +0000 |
commit | 506fc2356fd959269b75c4ffe817f669ca5fffc2 (patch) | |
tree | 2ceca88e658c890266246e36c28e902adf836880 /base | |
parent | fe58acc2305c2026a0a01195808dad673216e054 (diff) | |
download | chromium_src-506fc2356fd959269b75c4ffe817f669ca5fffc2.zip chromium_src-506fc2356fd959269b75c4ffe817f669ca5fffc2.tar.gz chromium_src-506fc2356fd959269b75c4ffe817f669ca5fffc2.tar.bz2 |
Make SequencedWorkerPool a TaskRunner
Add basic tests for TaskRunner implementations, and
instantiate it for SequencedWorkerPool.
BUG=114329,114327
TEST=
Review URL: https://chromiumcodereview.appspot.com/9401032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124087 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 2 | ||||
-rw-r--r-- | base/task_runner_test_template.cc | 23 | ||||
-rw-r--r-- | base/task_runner_test_template.h | 157 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool.cc | 25 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool.h | 21 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool_unittest.cc | 39 |
6 files changed, 261 insertions, 6 deletions
diff --git a/base/base.gyp b/base/base.gyp index bb85d8a..b5e4c71 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -223,6 +223,8 @@ 'sys_string_conversions_mac_unittest.mm', 'sys_string_conversions_unittest.cc', 'system_monitor/system_monitor_unittest.cc', + 'task_runner_test_template.cc', + 'task_runner_test_template.h', 'template_util_unittest.cc', 'test/trace_event_analyzer_unittest.cc', 'threading/non_thread_safe_unittest.cc', diff --git a/base/task_runner_test_template.cc b/base/task_runner_test_template.cc new file mode 100644 index 0000000..064eac3 --- /dev/null +++ b/base/task_runner_test_template.cc @@ -0,0 +1,23 @@ +// 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/task_runner_test_template.h" + +namespace base { + +TaskTracker::TaskTracker() {} + +TaskTracker::~TaskTracker() {} + +void TaskTracker::RunTask(int i) { + AutoLock lock(task_run_counts_lock_); + ++task_run_counts_[i]; +} + +std::map<int, int> TaskTracker::GetTaskRunCounts() const { + AutoLock lock(task_run_counts_lock_); + return task_run_counts_; +} + +} // namespace base diff --git a/base/task_runner_test_template.h b/base/task_runner_test_template.h new file mode 100644 index 0000000..012fa88 --- /dev/null +++ b/base/task_runner_test_template.h @@ -0,0 +1,157 @@ +// 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. + +// This class defines tests that implementations of TaskRunner should +// pass in order to be conformant. Here's how you use it to test your +// implementation. +// +// Say your class is called MyTaskRunner. Then you need to define a +// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc +// like this: +// +// class MyTaskRunnerTestDelegate { +// public: +// // Tasks posted to the task runner after this and before +// // StopTaskRunner() is called is called should run successfully. +// void StartTaskRunner() { +// ... +// } +// +// // Should return the task runner implementation. Only called +// // after StartTaskRunner and before StopTaskRunner. +// scoped_refptr<MyTaskRunner> GetTaskRunner() { +// ... +// } +// +// // Stop the task runner and make sure all tasks posted before +// // this is called are run. +// void StopTaskRunner() { +// ... +// } +// +// // Returns whether or not the task runner obeys non-zero delays. +// bool TaskRunnerHandlesNonZeroDelays() const { +// return true; +// } +// }; +// +// The TaskRunnerTest test harness will have a member variable of +// this delegate type and will call its functions in the various +// tests. +// +// Then you simply #include this file as well as gtest.h and add the +// following statement to my_task_runner_unittest.cc: +// +// INSTANTIATE_TYPED_TEST_CASE_P( +// MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate); +// +// Easy! + +#ifndef BASE_TASK_RUNNER_TEST_TEMPLATE_H_ +#define BASE_TASK_RUNNER_TEST_TEMPLATE_H_ +#pragma once + +#include <cstddef> +#include <map> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/task_runner.h" +#include "base/tracked_objects.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +// Utility class used in the tests below. +class TaskTracker : public RefCountedThreadSafe<TaskTracker> { + public: + TaskTracker(); + + void RunTask(int i); + + std::map<int, int> GetTaskRunCounts() const; + + private: + friend class RefCountedThreadSafe<TaskTracker>; + + ~TaskTracker(); + + mutable Lock task_run_counts_lock_; + std::map<int, int> task_run_counts_; + + DISALLOW_COPY_AND_ASSIGN(TaskTracker); +}; + +template <typename TaskRunnerTestDelegate> +class TaskRunnerTest : public testing::Test { + protected: + TaskRunnerTest() : task_tracker_(new TaskTracker()) {} + + const scoped_refptr<TaskTracker> task_tracker_; + TaskRunnerTestDelegate delegate_; +}; + +TYPED_TEST_CASE_P(TaskRunnerTest); + +// We can't really test much, since TaskRunner provides very few +// guarantees. + +// Post a bunch of tasks to the task runner. They should all +// complete. +TYPED_TEST_P(TaskRunnerTest, Basic) { + std::map<int, int> expected_task_run_counts; + + this->delegate_.StartTaskRunner(); + scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); + // Post each ith task i+1 times. + for (int i = 0; i < 20; ++i) { + Closure task = Bind(&TaskTracker::RunTask, this->task_tracker_, i); + for (int j = 0; j < i + 1; ++j) { + task_runner->PostTask(FROM_HERE, task); + ++expected_task_run_counts[i]; + } + } + this->delegate_.StopTaskRunner(); + + EXPECT_EQ(expected_task_run_counts, + this->task_tracker_->GetTaskRunCounts()); +} + +// Post a bunch of delayed tasks to the task runner. They should all +// complete. +TYPED_TEST_P(TaskRunnerTest, Delayed) { + if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) { + DLOG(INFO) << "This TaskRunner doesn't handle non-zero delays; skipping"; + return; + } + + std::map<int, int> expected_task_run_counts; + + this->delegate_.StartTaskRunner(); + scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); + // Post each ith task i+1 times with delays from 0-i. + for (int i = 0; i < 20; ++i) { + Closure task = Bind(&TaskTracker::RunTask, this->task_tracker_, i); + for (int j = 0; j < i + 1; ++j) { + task_runner->PostDelayedTask(FROM_HERE, task, j); + ++expected_task_run_counts[i]; + } + } + this->delegate_.StopTaskRunner(); + + EXPECT_EQ(expected_task_run_counts, + this->task_tracker_->GetTaskRunCounts()); +} + +// TODO(akalin): Add test to verify RunsTaskOnCurrentThread() returns +// true for tasks runs on the TaskRunner and returns false on a +// separate PlatformThread. + +REGISTER_TYPED_TEST_CASE_P(TaskRunnerTest, Basic, Delayed); + +} // namespace base + +#endif //#define BASE_TASK_RUNNER_TEST_TEMPLATE_H_ diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc index 7679c15..48bb2eb 100644 --- a/base/threading/sequenced_worker_pool.cc +++ b/base/threading/sequenced_worker_pool.cc @@ -689,6 +689,31 @@ bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior( from_here, task); } +bool SequencedWorkerPool::PostDelayedTask( + const tracked_objects::Location& from_here, + const Closure& task, + int64 delay_ms) { + // TODO(akalin): Add support for non-zero delays. + DCHECK_EQ(delay_ms, 0); + return PostWorkerTask(from_here, task); +} + +bool SequencedWorkerPool::PostDelayedTask( + const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay) { + // TODO(akalin): Add support for non-zero delays. + DCHECK_EQ(delay.InMillisecondsRoundedUp(), 0); + return PostWorkerTask(from_here, task); +} + +bool SequencedWorkerPool::RunsTasksOnCurrentThread() const { + // TODO(akalin): Keep track of the thread IDs of our worker threads + // and use those to implement this function. + NOTREACHED(); + return true; +} + void SequencedWorkerPool::FlushForTesting() { inner_->FlushForTesting(); } diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h index c840f07..de31828 100644 --- a/base/threading/sequenced_worker_pool.h +++ b/base/threading/sequenced_worker_pool.h @@ -14,6 +14,7 @@ #include "base/callback_forward.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/task_runner.h" namespace tracked_objects { class Location; @@ -51,8 +52,10 @@ namespace base { // not enforce shutdown semantics or allow us to specify how many worker // threads to run. For the typical use case of random background work, we don't // necessarily want to be super aggressive about creating threads. -class BASE_EXPORT SequencedWorkerPool - : public RefCountedThreadSafe<SequencedWorkerPool> { +// +// Note that SequencedWorkerPool is RefCountedThreadSafe (inherited +// from TaskRunner). +class BASE_EXPORT SequencedWorkerPool : public TaskRunner { public: // Defines what should happen to a task posted to the worker pool on shutdown. enum WorkerShutdown { @@ -191,6 +194,15 @@ class BASE_EXPORT SequencedWorkerPool const Closure& task, WorkerShutdown shutdown_behavior); + // TaskRunner implementation. Forwards to PostWorkerTask(). + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + const Closure& task, + int64 delay_ms) OVERRIDE; + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, + const Closure& task, + TimeDelta delay) OVERRIDE; + virtual bool RunsTasksOnCurrentThread() const OVERRIDE; + // Blocks until all pending tasks are complete. This should only be called in // unit tests when you want to validate something that should have happened. // @@ -210,14 +222,15 @@ class BASE_EXPORT SequencedWorkerPool // and ownership of the pointer is kept with the caller. void SetTestingObserver(TestingObserver* observer); + protected: + virtual ~SequencedWorkerPool(); + private: friend class RefCountedThreadSafe<SequencedWorkerPool>; class Inner; class Worker; - ~SequencedWorkerPool(); - // Avoid pulling in too many headers by putting everything into // |inner_|. const scoped_ptr<Inner> inner_; diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc index a7e9e93..02cb1c4 100644 --- a/base/threading/sequenced_worker_pool_unittest.cc +++ b/base/threading/sequenced_worker_pool_unittest.cc @@ -8,6 +8,7 @@ #include "base/memory/ref_counted.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" +#include "base/task_runner_test_template.h" #include "base/threading/platform_thread.h" #include "base/threading/sequenced_worker_pool.h" #include "base/tracked_objects.h" @@ -223,8 +224,6 @@ void EnsureTasksToCompleteCountAndUnblock(scoped_refptr<TestTracker> tracker, blocker->Unblock(threads_to_awake); } -} // namespace - // Tests that same-named tokens have the same ID. TEST_F(SequencedWorkerPoolTest, NamedTokens) { const std::string name1("hello"); @@ -403,4 +402,40 @@ TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) { EXPECT_EQ(1u, result.size()); } +class SequencedWorkerPoolTaskRunnerTestDelegate { + public: + SequencedWorkerPoolTaskRunnerTestDelegate() {} + + ~SequencedWorkerPoolTaskRunnerTestDelegate() {} + + void StartTaskRunner() { + worker_pool_ = + new SequencedWorkerPool(10, "SequencedWorkerPoolTaskRunnerTest"); + } + + scoped_refptr<SequencedWorkerPool> GetTaskRunner() { + return worker_pool_; + } + + void StopTaskRunner() { + worker_pool_->Shutdown(); + worker_pool_ = NULL; + } + + bool TaskRunnerHandlesNonZeroDelays() const { + // TODO(akalin): Set this to true once SequencedWorkerPool handles + // non-zero delays. + return false; + } + + private: + scoped_refptr<SequencedWorkerPool> worker_pool_; +}; + +INSTANTIATE_TYPED_TEST_CASE_P( + SequencedWorkerPool, TaskRunnerTest, + SequencedWorkerPoolTaskRunnerTestDelegate); + +} // namespace + } // namespace base |