summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-29 01:33:03 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-29 01:33:03 +0000
commit506fc2356fd959269b75c4ffe817f669ca5fffc2 (patch)
tree2ceca88e658c890266246e36c28e902adf836880 /base
parentfe58acc2305c2026a0a01195808dad673216e054 (diff)
downloadchromium_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.gyp2
-rw-r--r--base/task_runner_test_template.cc23
-rw-r--r--base/task_runner_test_template.h157
-rw-r--r--base/threading/sequenced_worker_pool.cc25
-rw-r--r--base/threading/sequenced_worker_pool.h21
-rw-r--r--base/threading/sequenced_worker_pool_unittest.cc39
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