Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1496)

Side by Side Diff: base/task_scheduler/thread_pool_unittest.cc

Issue 1708773002: TaskScheduler [7] SchedulerThreadPool (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@s_5_worker_thread
Patch Set: self review Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task_scheduler/thread_pool.h"
6
7 #include <stddef.h>
8
9 #include <unordered_set>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/macros.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/synchronization/condition_variable.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/task_runner.h"
19 #include "base/task_scheduler/scheduler_lock.h"
20 #include "base/task_scheduler/task_tracker.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/threading/simple_thread.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace base {
26 namespace internal {
27 namespace {
28
29 const size_t kNumRepetitions = 4;
30 const size_t kNumThreadsInThreadPool = 4;
31 const size_t kNumTaskRunners = 4;
32 const size_t kNumThreadsPostingTasksPerTaskRunner = 2;
33 const size_t kNumTasksPostedPerThread = 150;
34
35 class TaskSchedulerThreadPoolTest : public testing::Test {
36 protected:
37 TaskSchedulerThreadPoolTest() : cv_(lock_.CreateConditionVariable()) {}
38
39 void SetUp() override {
40 thread_pool_ = ThreadPool::CreateThreadPool(
41 ThreadPriority::NORMAL, kNumThreadsInThreadPool,
42 Bind(&TaskSchedulerThreadPoolTest::RanTaskFromSequenceCallback,
43 Unretained(this)),
44 &task_tracker_);
45 ASSERT_TRUE(thread_pool_);
46 }
47
48 void TearDown() override { thread_pool_->JoinForTesting(); }
49
50 // Waits for all tasks returned by CreateTask() to run.
51 void WaitForAllTasksToRun() {
52 AutoSchedulerLock auto_lock(lock_);
53 while (run_tasks_.size() < num_created_tasks_)
54 cv_->Wait();
55 }
56
57 size_t NumRunTasks() const {
58 AutoSchedulerLock auto_lock(lock_);
59 return run_tasks_.size();
60 }
61
62 scoped_ptr<ThreadPool> thread_pool_;
63
64 public:
65 // |task_runner| is the TaskRunner through which the task will be posted. If
66 // |post_nested_task| is true, the created task will post a new task through
67 // |task_runner| when it runs.
68 Closure CreateTask(scoped_refptr<TaskRunner> task_runner,
69 bool post_nested_task) {
70 AutoSchedulerLock auto_lock(lock_);
71 return Bind(&TaskSchedulerThreadPoolTest::RunTaskCallback, Unretained(this),
72 num_created_tasks_++, task_runner, post_nested_task);
73 }
74
75 private:
76 void RanTaskFromSequenceCallback(const SchedulerWorkerThread* worker_thread,
77 scoped_refptr<Sequence> sequence) {
78 if (!sequence->PopTask()) {
79 const SequenceSortKey sort_key(sequence->GetSortKey());
80 thread_pool_->ReinsertSequence(worker_thread, std::move(sequence),
81 sort_key);
82 }
83 }
84
85 // |task_index| is a unique index for this task. |task_runner| is the
86 // TaskRunner through which the task was posted. If |post_nested_task| is
87 // true, the callback will post a new task through |task_runner|.
88 void RunTaskCallback(size_t task_index,
89 scoped_refptr<TaskRunner> task_runner,
90 bool post_nested_task) {
91 if (post_nested_task)
92 task_runner->PostTask(FROM_HERE, CreateTask(task_runner, false));
93
94 EXPECT_TRUE(task_runner->RunsTasksOnCurrentThread());
95
96 AutoSchedulerLock auto_lock(lock_);
97
98 if (run_tasks_.find(task_index) != run_tasks_.end())
99 ADD_FAILURE() << "A task ran more than once.";
100 run_tasks_.insert(task_index);
101
102 cv_->Signal();
103 }
104
105 TaskTracker task_tracker_;
106
107 // Synchronizes access to all members below.
108 mutable SchedulerLock lock_;
109
110 // Condition variable signaled when a task completes its execution.
111 scoped_ptr<ConditionVariable> cv_;
112
113 // Number of tasks returned by CreateTask().
114 size_t num_created_tasks_ = 0;
115
116 // Indexes of tasks that ran.
117 std::unordered_set<size_t> run_tasks_;
118
119 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerThreadPoolTest);
120 };
121
122 class ThreadPostingTasks : public SimpleThread {
123 public:
124 // If |post_nested_task| is true, each task posted by this thread will post
125 // another task when it runs.
126 ThreadPostingTasks(scoped_refptr<TaskRunner> task_runner,
127 bool post_nested_task,
128 TaskSchedulerThreadPoolTest* test)
129 : SimpleThread("ThreadPostingTasks"),
130 task_runner_(std::move(task_runner)),
131 post_nested_task_(post_nested_task),
132 test_(test) {}
133
134 private:
135 void Run() override {
136 EXPECT_FALSE(task_runner_->RunsTasksOnCurrentThread());
137
138 for (size_t i = 0; i < kNumTasksPostedPerThread; ++i) {
139 task_runner_->PostTask(
140 FROM_HERE, test_->CreateTask(task_runner_, post_nested_task_));
141 }
142 }
143
144 scoped_refptr<TaskRunner> task_runner_;
145 const bool post_nested_task_;
146 TaskSchedulerThreadPoolTest* const test_;
147
148 DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks);
149 };
150
151 TEST_F(TaskSchedulerThreadPoolTest, PostParallelTasks) {
152 // Repeat the test |kNumRepetitions| times, waiting for all
153 // SchedulerWorkerThreads to become idle between each repetition. This ensures
154 // that TaskRunners can wake up SchedulerWorkerThreads that have added
155 // themselves to the stack of idle SchedulerWorkerThreads of the ThreadPool.
156 for (size_t i = 0; i < kNumRepetitions; ++i) {
157 // Create |kNumTaskRunners| * |kNumThreadsPostingTasksPerTaskRunner| threads
158 // that will post tasks to |kNumTaskRunners| PARALLEL TaskRunners.
159 std::vector<scoped_ptr<ThreadPostingTasks>> threads_posting_tasks;
160 for (size_t j = 0; j < kNumTaskRunners; ++j) {
161 scoped_refptr<TaskRunner> task_runner =
162 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(),
163 ExecutionMode::PARALLEL);
164 for (size_t k = 0; k < kNumThreadsPostingTasksPerTaskRunner; ++k) {
165 threads_posting_tasks.push_back(
166 make_scoped_ptr(new ThreadPostingTasks(task_runner, false, this)));
167 threads_posting_tasks.back()->Start();
168 }
169 }
170
171 for (const auto& thread_posting_tasks : threads_posting_tasks)
172 thread_posting_tasks->Join();
173
174 WaitForAllTasksToRun();
175 EXPECT_EQ((i + 1) * kNumTaskRunners * kNumThreadsPostingTasksPerTaskRunner *
176 kNumTasksPostedPerThread,
177 NumRunTasks());
178 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
179 }
180 }
181
182 TEST_F(TaskSchedulerThreadPoolTest, PostParallelTasksWithNestedPostTasks) {
183 // Repeat the test |kNumRepetitions| times, waiting for all
184 // SchedulerWorkerThreads to become idle between each repetition. This ensures
185 // that TaskRunners can wake up SchedulerWorkerThreads that have added
186 // themselves to the stack of idle SchedulerWorkerThreads of the ThreadPool.
187 for (size_t i = 0; i < kNumRepetitions; ++i) {
188 // Create |kNumTaskRunners| * |kNumThreadsPostingTasksPerTaskRunner| threads
189 // that will post tasks to |kNumTaskRunners| PARALLEL TaskRunners. Each task
190 // posted by these threads will post another task when it runs.
191 std::vector<scoped_ptr<ThreadPostingTasks>> threads_posting_tasks;
192 for (size_t j = 0; j < kNumTaskRunners; ++j) {
193 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits(
194 TaskTraits(), ExecutionMode::PARALLEL);
195 for (size_t k = 0; k < kNumThreadsPostingTasksPerTaskRunner; ++k) {
196 threads_posting_tasks.push_back(
197 make_scoped_ptr(new ThreadPostingTasks(task_runner, true, this)));
198 threads_posting_tasks.back()->Start();
199 }
200 }
201
202 for (const auto& thread_posting_tasks : threads_posting_tasks)
203 thread_posting_tasks->Join();
204
205 WaitForAllTasksToRun();
206 EXPECT_EQ(2 * (i + 1) * kNumTaskRunners *
207 kNumThreadsPostingTasksPerTaskRunner *
208 kNumTasksPostedPerThread,
209 NumRunTasks());
210 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
211 }
212 }
213
214 TEST_F(TaskSchedulerThreadPoolTest, PostParallelTasksWithBlockedTasks) {
215 // Post |kNumThreadsInThreadPool| - 1 tasks that block until |event| is
216 // signaled.
217 WaitableEvent event(
218 true, // Manual reset, to be able to wake up multiple waiters at once.
219 false); // Not signaled initially.
220 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits(
221 TaskTraits(), ExecutionMode::PARALLEL);
222 for (size_t i = 0; i < (kNumThreadsInThreadPool - 1); ++i) {
223 task_runner->PostTask(FROM_HERE,
224 Bind(&WaitableEvent::Wait, Unretained(&event)));
225 }
226
227 // Post |kNumTasksPostedPerThread| tasks that should run despite the fact
228 // that all threads in |thread_pool_| are busy except one.
229 for (size_t i = 0; i < kNumTasksPostedPerThread; ++i)
230 task_runner->PostTask(FROM_HERE, CreateTask(task_runner, false));
231
232 WaitForAllTasksToRun();
233 event.Signal();
234 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
235 }
robliao 2016/03/31 22:48:56 Would it be useful to have a test that saturates t
fdoray 2016/04/01 16:02:52 Done. TaskSchedulerThreadPoolTest.Saturate
236
237 } // namespace
238 } // namespace internal
239 } // namespace base
OLDNEW
« base/task_scheduler/thread_pool.cc ('K') | « base/task_scheduler/thread_pool.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698