blob: 8c913f7b4403875b29dde89c72c19c8f70cf355c [file] [log] [blame]
fdoraya2d271b2016-04-15 23:09:08 +09001// 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
fdoray9b0b2332016-04-26 06:34:33 +09005#include "base/task_scheduler/scheduler_thread_pool_impl.h"
fdoraya2d271b2016-04-15 23:09:08 +09006
7#include <stddef.h>
8
9#include <memory>
10#include <unordered_set>
11#include <vector>
12
13#include "base/bind.h"
14#include "base/bind_helpers.h"
fdoraydace22d2016-04-29 04:35:47 +090015#include "base/callback.h"
fdoraya2d271b2016-04-15 23:09:08 +090016#include "base/macros.h"
17#include "base/memory/ptr_util.h"
18#include "base/memory/ref_counted.h"
19#include "base/synchronization/condition_variable.h"
20#include "base/synchronization/lock.h"
21#include "base/synchronization/waitable_event.h"
22#include "base/task_runner.h"
fdorayc2c74992016-04-20 10:39:21 +090023#include "base/task_scheduler/delayed_task_manager.h"
fdoraya2d271b2016-04-15 23:09:08 +090024#include "base/task_scheduler/sequence.h"
25#include "base/task_scheduler/sequence_sort_key.h"
26#include "base/task_scheduler/task_tracker.h"
fdoray570633b2016-04-26 01:24:46 +090027#include "base/task_scheduler/test_task_factory.h"
fdoraya2d271b2016-04-15 23:09:08 +090028#include "base/threading/platform_thread.h"
29#include "base/threading/simple_thread.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32namespace base {
33namespace internal {
34namespace {
35
36const size_t kNumThreadsInThreadPool = 4;
37const size_t kNumThreadsPostingTasks = 4;
38const size_t kNumTasksPostedPerThread = 150;
39
fdoray9b0b2332016-04-26 06:34:33 +090040class TestDelayedTaskManager : public DelayedTaskManager {
41 public:
42 TestDelayedTaskManager() : DelayedTaskManager(Bind(&DoNothing)) {}
43
44 void SetCurrentTime(TimeTicks now) { now_ = now; }
45
46 // DelayedTaskManager:
47 TimeTicks Now() const override { return now_; }
48
49 private:
50 TimeTicks now_ = TimeTicks::Now();
51
52 DISALLOW_COPY_AND_ASSIGN(TestDelayedTaskManager);
53};
54
55class TaskSchedulerThreadPoolImplTest
fdoray82243232016-04-16 08:25:15 +090056 : public testing::TestWithParam<ExecutionMode> {
fdoraya2d271b2016-04-15 23:09:08 +090057 protected:
fdoray9b0b2332016-04-26 06:34:33 +090058 TaskSchedulerThreadPoolImplTest() = default;
fdoraya2d271b2016-04-15 23:09:08 +090059
60 void SetUp() override {
fdorayde70fef2016-04-26 10:52:07 +090061 thread_pool_ = SchedulerThreadPoolImpl::Create(
fdoraya2d271b2016-04-15 23:09:08 +090062 ThreadPriority::NORMAL, kNumThreadsInThreadPool,
fdoray9b0b2332016-04-26 06:34:33 +090063 Bind(&TaskSchedulerThreadPoolImplTest::ReEnqueueSequenceCallback,
fdoraya2d271b2016-04-15 23:09:08 +090064 Unretained(this)),
fdorayc2c74992016-04-20 10:39:21 +090065 &task_tracker_, &delayed_task_manager_);
fdoraya2d271b2016-04-15 23:09:08 +090066 ASSERT_TRUE(thread_pool_);
67 }
68
69 void TearDown() override {
70 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
71 thread_pool_->JoinForTesting();
72 }
73
fdoray9b0b2332016-04-26 06:34:33 +090074 std::unique_ptr<SchedulerThreadPoolImpl> thread_pool_;
fdoraya2d271b2016-04-15 23:09:08 +090075
76 TaskTracker task_tracker_;
fdoray9b0b2332016-04-26 06:34:33 +090077 TestDelayedTaskManager delayed_task_manager_;
fdoraya2d271b2016-04-15 23:09:08 +090078
fdoray9b0b2332016-04-26 06:34:33 +090079 private:
80 void ReEnqueueSequenceCallback(scoped_refptr<Sequence> sequence) {
81 // In production code, this callback would be implemented by the
82 // TaskScheduler which would first determine which PriorityQueue the
83 // sequence must be re-enqueued.
84 const SequenceSortKey sort_key(sequence->GetSortKey());
85 thread_pool_->ReEnqueueSequence(std::move(sequence), sort_key);
86 }
87
88 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerThreadPoolImplTest);
fdoraya2d271b2016-04-15 23:09:08 +090089};
90
fdoray570633b2016-04-26 01:24:46 +090091using PostNestedTask = test::TestTaskFactory::PostNestedTask;
fdoraya2d271b2016-04-15 23:09:08 +090092
93class ThreadPostingTasks : public SimpleThread {
94 public:
fdoray570633b2016-04-26 01:24:46 +090095 enum class WaitBeforePostTask {
96 NO_WAIT,
97 WAIT_FOR_ALL_THREADS_IDLE,
98 };
99
fdoraya2d271b2016-04-15 23:09:08 +0900100 // Constructs a thread that posts tasks to |thread_pool| through an
fdoray570633b2016-04-26 01:24:46 +0900101 // |execution_mode| task runner. If |wait_before_post_task| is
102 // WAIT_FOR_ALL_THREADS_IDLE, the thread waits until all worker threads in
103 // |thread_pool| are idle before posting a new task. If |post_nested_task| is
104 // YES, each task posted by this thread posts another task when it runs.
fdoray9b0b2332016-04-26 06:34:33 +0900105 ThreadPostingTasks(SchedulerThreadPoolImpl* thread_pool,
fdoraya2d271b2016-04-15 23:09:08 +0900106 ExecutionMode execution_mode,
fdoray570633b2016-04-26 01:24:46 +0900107 WaitBeforePostTask wait_before_post_task,
108 PostNestedTask post_nested_task)
fdoraya2d271b2016-04-15 23:09:08 +0900109 : SimpleThread("ThreadPostingTasks"),
110 thread_pool_(thread_pool),
fdoray570633b2016-04-26 01:24:46 +0900111 wait_before_post_task_(wait_before_post_task),
fdoray82243232016-04-16 08:25:15 +0900112 post_nested_task_(post_nested_task),
fdoray570633b2016-04-26 01:24:46 +0900113 factory_(thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(),
114 execution_mode),
115 execution_mode) {
fdoraya2d271b2016-04-15 23:09:08 +0900116 DCHECK(thread_pool_);
117 }
118
fdoray570633b2016-04-26 01:24:46 +0900119 const test::TestTaskFactory* factory() const { return &factory_; }
fdoraya2d271b2016-04-15 23:09:08 +0900120
121 private:
122 void Run() override {
fdoray82243232016-04-16 08:25:15 +0900123 EXPECT_FALSE(factory_.task_runner()->RunsTasksOnCurrentThread());
fdoraya2d271b2016-04-15 23:09:08 +0900124
125 for (size_t i = 0; i < kNumTasksPostedPerThread; ++i) {
fdoray570633b2016-04-26 01:24:46 +0900126 if (wait_before_post_task_ ==
127 WaitBeforePostTask::WAIT_FOR_ALL_THREADS_IDLE) {
fdoraya2d271b2016-04-15 23:09:08 +0900128 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
fdoray570633b2016-04-26 01:24:46 +0900129 }
fdoraydace22d2016-04-29 04:35:47 +0900130 EXPECT_TRUE(factory_.PostTask(post_nested_task_, Closure()));
fdoraya2d271b2016-04-15 23:09:08 +0900131 }
132 }
133
fdoray9b0b2332016-04-26 06:34:33 +0900134 SchedulerThreadPoolImpl* const thread_pool_;
fdoraya2d271b2016-04-15 23:09:08 +0900135 const scoped_refptr<TaskRunner> task_runner_;
fdoray570633b2016-04-26 01:24:46 +0900136 const WaitBeforePostTask wait_before_post_task_;
137 const PostNestedTask post_nested_task_;
138 test::TestTaskFactory factory_;
fdoraya2d271b2016-04-15 23:09:08 +0900139
140 DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks);
141};
142
fdoray570633b2016-04-26 01:24:46 +0900143using WaitBeforePostTask = ThreadPostingTasks::WaitBeforePostTask;
144
fdoray9b0b2332016-04-26 06:34:33 +0900145void ShouldNotRunCallback() {
146 ADD_FAILURE() << "Ran a task that shouldn't run.";
147}
148
149void SignalEventCallback(WaitableEvent* event) {
150 DCHECK(event);
151 event->Signal();
152}
153
154} // namespace
155
156TEST_P(TaskSchedulerThreadPoolImplTest, PostTasks) {
fdoray82243232016-04-16 08:25:15 +0900157 // Create threads to post tasks.
fdoraya2d271b2016-04-15 23:09:08 +0900158 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
159 for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
fdoray570633b2016-04-26 01:24:46 +0900160 threads_posting_tasks.push_back(WrapUnique(new ThreadPostingTasks(
161 thread_pool_.get(), GetParam(), WaitBeforePostTask::NO_WAIT,
162 PostNestedTask::NO)));
fdoraya2d271b2016-04-15 23:09:08 +0900163 threads_posting_tasks.back()->Start();
164 }
165
166 // Wait for all tasks to run.
167 for (const auto& thread_posting_tasks : threads_posting_tasks) {
168 thread_posting_tasks->Join();
169 thread_posting_tasks->factory()->WaitForAllTasksToRun();
fdoraya2d271b2016-04-15 23:09:08 +0900170 }
171
172 // Wait until all worker threads are idle to be sure that no task accesses
fdoray570633b2016-04-26 01:24:46 +0900173 // its TestTaskFactory after |thread_posting_tasks| is destroyed.
fdoraya2d271b2016-04-15 23:09:08 +0900174 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
175}
176
fdoray9b0b2332016-04-26 06:34:33 +0900177TEST_P(TaskSchedulerThreadPoolImplTest, PostTasksWaitAllThreadsIdle) {
fdoray82243232016-04-16 08:25:15 +0900178 // Create threads to post tasks. To verify that worker threads can sleep and
179 // be woken up when new tasks are posted, wait for all threads to become idle
180 // before posting a new task.
fdoraya2d271b2016-04-15 23:09:08 +0900181 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
182 for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
fdoray570633b2016-04-26 01:24:46 +0900183 threads_posting_tasks.push_back(WrapUnique(new ThreadPostingTasks(
184 thread_pool_.get(), GetParam(),
185 WaitBeforePostTask::WAIT_FOR_ALL_THREADS_IDLE, PostNestedTask::NO)));
fdoraya2d271b2016-04-15 23:09:08 +0900186 threads_posting_tasks.back()->Start();
187 }
188
189 // Wait for all tasks to run.
190 for (const auto& thread_posting_tasks : threads_posting_tasks) {
191 thread_posting_tasks->Join();
192 thread_posting_tasks->factory()->WaitForAllTasksToRun();
fdoraya2d271b2016-04-15 23:09:08 +0900193 }
194
195 // Wait until all worker threads are idle to be sure that no task accesses
fdoray570633b2016-04-26 01:24:46 +0900196 // its TestTaskFactory after |thread_posting_tasks| is destroyed.
fdoraya2d271b2016-04-15 23:09:08 +0900197 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
198}
199
fdoray9b0b2332016-04-26 06:34:33 +0900200TEST_P(TaskSchedulerThreadPoolImplTest, NestedPostTasks) {
fdoray82243232016-04-16 08:25:15 +0900201 // Create threads to post tasks. Each task posted by these threads will post
202 // another task when it runs.
fdoraya2d271b2016-04-15 23:09:08 +0900203 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
204 for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
fdoray570633b2016-04-26 01:24:46 +0900205 threads_posting_tasks.push_back(WrapUnique(new ThreadPostingTasks(
206 thread_pool_.get(), GetParam(), WaitBeforePostTask::NO_WAIT,
207 PostNestedTask::YES)));
fdoraya2d271b2016-04-15 23:09:08 +0900208 threads_posting_tasks.back()->Start();
209 }
210
211 // Wait for all tasks to run.
212 for (const auto& thread_posting_tasks : threads_posting_tasks) {
213 thread_posting_tasks->Join();
214 thread_posting_tasks->factory()->WaitForAllTasksToRun();
fdoraya2d271b2016-04-15 23:09:08 +0900215 }
216
217 // Wait until all worker threads are idle to be sure that no task accesses
fdoray570633b2016-04-26 01:24:46 +0900218 // its TestTaskFactory after |thread_posting_tasks| is destroyed.
fdoraya2d271b2016-04-15 23:09:08 +0900219 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
220}
221
fdoray9b0b2332016-04-26 06:34:33 +0900222TEST_P(TaskSchedulerThreadPoolImplTest, PostTasksWithOneAvailableThread) {
fdoraydace22d2016-04-29 04:35:47 +0900223 // Post blocking tasks to keep all threads busy except one until |event| is
224 // signaled. Use different factories so that tasks are added to different
225 // sequences and can run simultaneously when the execution mode is SEQUENCED.
fdoraya2d271b2016-04-15 23:09:08 +0900226 WaitableEvent event(true, false);
fdoray570633b2016-04-26 01:24:46 +0900227 std::vector<std::unique_ptr<test::TestTaskFactory>> blocked_task_factories;
fdoray82243232016-04-16 08:25:15 +0900228 for (size_t i = 0; i < (kNumThreadsInThreadPool - 1); ++i) {
fdoray570633b2016-04-26 01:24:46 +0900229 blocked_task_factories.push_back(WrapUnique(new test::TestTaskFactory(
230 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), GetParam()),
231 GetParam())));
fdoraydace22d2016-04-29 04:35:47 +0900232 EXPECT_TRUE(blocked_task_factories.back()->PostTask(
233 PostNestedTask::NO, Bind(&WaitableEvent::Wait, Unretained(&event))));
fdoray82243232016-04-16 08:25:15 +0900234 blocked_task_factories.back()->WaitForAllTasksToRun();
235 }
fdoraya2d271b2016-04-15 23:09:08 +0900236
237 // Post |kNumTasksPostedPerThread| tasks that should all run despite the fact
238 // that only one thread in |thread_pool_| isn't busy.
fdoray570633b2016-04-26 01:24:46 +0900239 test::TestTaskFactory short_task_factory(
240 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), GetParam()),
241 GetParam());
fdoraya2d271b2016-04-15 23:09:08 +0900242 for (size_t i = 0; i < kNumTasksPostedPerThread; ++i)
fdoraydace22d2016-04-29 04:35:47 +0900243 EXPECT_TRUE(short_task_factory.PostTask(PostNestedTask::NO, Closure()));
fdoray82243232016-04-16 08:25:15 +0900244 short_task_factory.WaitForAllTasksToRun();
fdoraya2d271b2016-04-15 23:09:08 +0900245
246 // Release tasks waiting on |event|.
247 event.Signal();
248
249 // Wait until all worker threads are idle to be sure that no task accesses
fdoray570633b2016-04-26 01:24:46 +0900250 // its TestTaskFactory after it is destroyed.
fdoraya2d271b2016-04-15 23:09:08 +0900251 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
252}
253
fdoray9b0b2332016-04-26 06:34:33 +0900254TEST_P(TaskSchedulerThreadPoolImplTest, Saturate) {
fdoray82243232016-04-16 08:25:15 +0900255 // Verify that it is possible to have |kNumThreadsInThreadPool|
fdoraydace22d2016-04-29 04:35:47 +0900256 // tasks/sequences running simultaneously. Use different factories so that the
257 // blocking tasks are added to different sequences and can run simultaneously
258 // when the execution mode is SEQUENCED.
fdoraya2d271b2016-04-15 23:09:08 +0900259 WaitableEvent event(true, false);
fdoray570633b2016-04-26 01:24:46 +0900260 std::vector<std::unique_ptr<test::TestTaskFactory>> factories;
fdoray82243232016-04-16 08:25:15 +0900261 for (size_t i = 0; i < kNumThreadsInThreadPool; ++i) {
fdoray570633b2016-04-26 01:24:46 +0900262 factories.push_back(WrapUnique(new test::TestTaskFactory(
263 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), GetParam()),
264 GetParam())));
fdoraydace22d2016-04-29 04:35:47 +0900265 EXPECT_TRUE(factories.back()->PostTask(
266 PostNestedTask::NO, Bind(&WaitableEvent::Wait, Unretained(&event))));
fdoray82243232016-04-16 08:25:15 +0900267 factories.back()->WaitForAllTasksToRun();
268 }
fdoraya2d271b2016-04-15 23:09:08 +0900269
270 // Release tasks waiting on |event|.
271 event.Signal();
272
273 // Wait until all worker threads are idle to be sure that no task accesses
fdoray570633b2016-04-26 01:24:46 +0900274 // its TestTaskFactory after it is destroyed.
fdoraya2d271b2016-04-15 23:09:08 +0900275 thread_pool_->WaitForAllWorkerThreadsIdleForTesting();
276}
277
fdoray9b0b2332016-04-26 06:34:33 +0900278// Verify that a Task can't be posted after shutdown.
279TEST_P(TaskSchedulerThreadPoolImplTest, PostTaskAfterShutdown) {
280 auto task_runner =
281 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), GetParam());
282 task_tracker_.Shutdown();
283 EXPECT_FALSE(task_runner->PostTask(FROM_HERE, Bind(&ShouldNotRunCallback)));
284}
285
286// Verify that a Task posted with a delay is added to the DelayedTaskManager and
287// doesn't run before its delay expires.
288TEST_P(TaskSchedulerThreadPoolImplTest, PostDelayedTask) {
289 EXPECT_TRUE(delayed_task_manager_.GetDelayedRunTime().is_null());
290
291 // Post a delayed task.
292 WaitableEvent task_ran(true, false);
293 EXPECT_TRUE(thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), GetParam())
294 ->PostDelayedTask(FROM_HERE, Bind(&SignalEventCallback,
295 Unretained(&task_ran)),
296 TimeDelta::FromSeconds(10)));
297
298 // The task should have been added to the DelayedTaskManager.
299 EXPECT_FALSE(delayed_task_manager_.GetDelayedRunTime().is_null());
300
301 // The task shouldn't run.
302 EXPECT_FALSE(task_ran.IsSignaled());
303
304 // Fast-forward time and post tasks that are ripe for execution.
305 delayed_task_manager_.SetCurrentTime(
306 delayed_task_manager_.GetDelayedRunTime());
307 delayed_task_manager_.PostReadyTasks();
308
309 // The task should run.
310 task_ran.Wait();
311}
312
fdoray82243232016-04-16 08:25:15 +0900313INSTANTIATE_TEST_CASE_P(Parallel,
fdoray9b0b2332016-04-26 06:34:33 +0900314 TaskSchedulerThreadPoolImplTest,
fdoray82243232016-04-16 08:25:15 +0900315 ::testing::Values(ExecutionMode::PARALLEL));
316INSTANTIATE_TEST_CASE_P(Sequenced,
fdoray9b0b2332016-04-26 06:34:33 +0900317 TaskSchedulerThreadPoolImplTest,
fdoray82243232016-04-16 08:25:15 +0900318 ::testing::Values(ExecutionMode::SEQUENCED));
fdorayf0683ab2016-04-29 00:45:35 +0900319INSTANTIATE_TEST_CASE_P(SingleThreaded,
320 TaskSchedulerThreadPoolImplTest,
321 ::testing::Values(ExecutionMode::SINGLE_THREADED));
fdoray82243232016-04-16 08:25:15 +0900322
fdoraya2d271b2016-04-15 23:09:08 +0900323} // namespace internal
324} // namespace base