blob: 242a921ed649ceb94d01c64c4f3cc4f2d9215f5d [file] [log] [blame]
Jeffrey He68d29bc2017-08-24 02:14:16 +09001// Copyright 2017 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/scheduler_worker_pool.h"
6
7#include <memory>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/location.h"
12#include "base/memory/ref_counted.h"
13#include "base/task_runner.h"
14#include "base/task_scheduler/delayed_task_manager.h"
15#include "base/task_scheduler/scheduler_worker_pool_impl.h"
16#include "base/task_scheduler/scheduler_worker_pool_params.h"
17#include "base/task_scheduler/task_tracker.h"
18#include "base/task_scheduler/task_traits.h"
19#include "base/task_scheduler/test_task_factory.h"
20#include "base/task_scheduler/test_utils.h"
21#include "base/test/test_timeouts.h"
22#include "base/threading/platform_thread.h"
23#include "base/threading/simple_thread.h"
24#include "base/threading/thread.h"
Jeffrey He71518662017-08-25 04:54:13 +090025#include "build/build_config.h"
Jeffrey He68d29bc2017-08-24 02:14:16 +090026#include "testing/gtest/include/gtest/gtest.h"
27
Jeffrey He71518662017-08-25 04:54:13 +090028#if defined(OS_WIN)
29#include "base/task_scheduler/platform_native_worker_pool_win.h"
30#endif
31
Jeffrey He68d29bc2017-08-24 02:14:16 +090032namespace base {
33namespace internal {
34
35namespace {
36
37constexpr size_t kNumWorkersInWorkerPool = 4;
38constexpr size_t kNumThreadsPostingTasks = 4;
39constexpr size_t kNumTasksPostedPerThread = 150;
40
Jeffrey He71518662017-08-25 04:54:13 +090041enum class PoolType {
42 GENERIC,
43#if defined(OS_WIN)
44 WINDOWS,
45#endif
46};
Jeffrey He68d29bc2017-08-24 02:14:16 +090047
48struct PoolExecutionType {
49 PoolType pool_type;
50 test::ExecutionMode execution_mode;
51};
52
53using PostNestedTask = test::TestTaskFactory::PostNestedTask;
54
55class ThreadPostingTasks : public SimpleThread {
56 public:
57 // Constructs a thread that posts |num_tasks_posted_per_thread| tasks to
58 // |worker_pool| through an |execution_mode| task runner. If
59 // |post_nested_task| is YES, each task posted by this thread posts another
60 // task when it runs.
61 ThreadPostingTasks(SchedulerWorkerPool* worker_pool,
62 test::ExecutionMode execution_mode,
63 PostNestedTask post_nested_task)
64 : SimpleThread("ThreadPostingTasks"),
65 worker_pool_(worker_pool),
66 post_nested_task_(post_nested_task),
67 factory_(test::CreateTaskRunnerWithExecutionMode(worker_pool,
68 execution_mode),
69 execution_mode) {
70 DCHECK(worker_pool_);
71 }
72
73 const test::TestTaskFactory* factory() const { return &factory_; }
74
75 private:
76 void Run() override {
77 EXPECT_FALSE(factory_.task_runner()->RunsTasksInCurrentSequence());
78
79 for (size_t i = 0; i < kNumTasksPostedPerThread; ++i)
80 EXPECT_TRUE(factory_.PostTask(post_nested_task_, Closure()));
81 }
82
83 SchedulerWorkerPool* const worker_pool_;
84 const scoped_refptr<TaskRunner> task_runner_;
85 const PostNestedTask post_nested_task_;
86 test::TestTaskFactory factory_;
87
88 DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks);
89};
90
91class TaskSchedulerWorkerPoolTest
92 : public testing::TestWithParam<PoolExecutionType> {
93 protected:
94 TaskSchedulerWorkerPoolTest()
95 : service_thread_("TaskSchedulerServiceThread") {}
96
97 void SetUp() override {
98 service_thread_.Start();
99 delayed_task_manager_.Start(service_thread_.task_runner());
100 CreateWorkerPool();
101 }
102
103 void TearDown() override {
104 service_thread_.Stop();
Gabriel Charettec37bf442017-09-21 11:26:13 +0900105 if (worker_pool_)
106 worker_pool_->JoinForTesting();
Jeffrey He68d29bc2017-08-24 02:14:16 +0900107 }
108
109 void CreateWorkerPool() {
110 ASSERT_FALSE(worker_pool_);
111 switch (GetParam().pool_type) {
112 case PoolType::GENERIC:
113 worker_pool_ = std::make_unique<SchedulerWorkerPoolImpl>(
Gabriel Charettee1640632018-01-19 01:56:31 +0900114 "TestWorkerPool", "A", ThreadPriority::NORMAL, &task_tracker_,
Jeffrey He68d29bc2017-08-24 02:14:16 +0900115 &delayed_task_manager_);
116 break;
Jeffrey He71518662017-08-25 04:54:13 +0900117#if defined(OS_WIN)
118 case PoolType::WINDOWS:
119 worker_pool_ = std::make_unique<PlatformNativeWorkerPoolWin>(
120 &task_tracker_, &delayed_task_manager_);
121 break;
122#endif
Jeffrey He68d29bc2017-08-24 02:14:16 +0900123 }
124 ASSERT_TRUE(worker_pool_);
125 }
126
127 void StartWorkerPool() {
128 ASSERT_TRUE(worker_pool_);
129 switch (GetParam().pool_type) {
130 case PoolType::GENERIC: {
131 SchedulerWorkerPoolImpl* scheduler_worker_pool_impl =
132 static_cast<SchedulerWorkerPoolImpl*>(worker_pool_.get());
Francois Dorayb9223752017-09-06 03:44:43 +0900133 scheduler_worker_pool_impl->Start(
134 SchedulerWorkerPoolParams(kNumWorkersInWorkerPool,
135 TimeDelta::Max()),
Robert Liao4eaab522017-11-02 05:06:03 +0900136 service_thread_.task_runner(),
137 SchedulerWorkerPoolImpl::WorkerEnvironment::NONE);
Jeffrey He68d29bc2017-08-24 02:14:16 +0900138 break;
139 }
Jeffrey He71518662017-08-25 04:54:13 +0900140#if defined(OS_WIN)
141 case PoolType::WINDOWS: {
142 PlatformNativeWorkerPoolWin* scheduler_worker_pool_windows_impl =
143 static_cast<PlatformNativeWorkerPoolWin*>(worker_pool_.get());
144 scheduler_worker_pool_windows_impl->Start();
145 break;
146 }
147#endif
Jeffrey He68d29bc2017-08-24 02:14:16 +0900148 }
149 }
150
151 std::unique_ptr<SchedulerWorkerPool> worker_pool_;
152
Gabriel Charette19075392018-01-19 20:00:53 +0900153 TaskTracker task_tracker_ = {"Test"};
Jeffrey He68d29bc2017-08-24 02:14:16 +0900154 Thread service_thread_;
155 DelayedTaskManager delayed_task_manager_;
156
157 private:
158 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolTest);
159};
160
161void ShouldNotRun() {
162 ADD_FAILURE() << "Ran a task that shouldn't run.";
163}
164
165} // namespace
166
167TEST_P(TaskSchedulerWorkerPoolTest, PostTasks) {
168 StartWorkerPool();
169 // Create threads to post tasks.
170 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
171 for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
172 threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>(
173 worker_pool_.get(), GetParam().execution_mode, PostNestedTask::NO));
174 threads_posting_tasks.back()->Start();
175 }
176
177 // Wait for all tasks to run.
178 for (const auto& thread_posting_tasks : threads_posting_tasks) {
179 thread_posting_tasks->Join();
180 thread_posting_tasks->factory()->WaitForAllTasksToRun();
181 }
182
183 // Flush the task tracker to be sure that no task accesses its TestTaskFactory
184 // after |thread_posting_tasks| is destroyed.
Robert Liaoe25acff2018-01-25 07:39:17 +0900185 task_tracker_.FlushForTesting();
Jeffrey He68d29bc2017-08-24 02:14:16 +0900186}
187
188TEST_P(TaskSchedulerWorkerPoolTest, NestedPostTasks) {
189 StartWorkerPool();
190 // Create threads to post tasks. Each task posted by these threads will post
191 // another task when it runs.
192 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
193 for (size_t i = 0; i < kNumThreadsPostingTasks; ++i) {
194 threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>(
195 worker_pool_.get(), GetParam().execution_mode, PostNestedTask::YES));
196 threads_posting_tasks.back()->Start();
197 }
198
199 // Wait for all tasks to run.
200 for (const auto& thread_posting_tasks : threads_posting_tasks) {
201 thread_posting_tasks->Join();
202 thread_posting_tasks->factory()->WaitForAllTasksToRun();
203 }
204
205 // Flush the task tracker to be sure that no task accesses its TestTaskFactory
206 // after |thread_posting_tasks| is destroyed.
Robert Liaoe25acff2018-01-25 07:39:17 +0900207 task_tracker_.FlushForTesting();
Jeffrey He68d29bc2017-08-24 02:14:16 +0900208}
209
210// Verify that a Task can't be posted after shutdown.
211TEST_P(TaskSchedulerWorkerPoolTest, PostTaskAfterShutdown) {
212 StartWorkerPool();
213 auto task_runner = test::CreateTaskRunnerWithExecutionMode(
214 worker_pool_.get(), GetParam().execution_mode);
215 task_tracker_.Shutdown();
216 EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
217}
218
Gabriel Charettec37bf442017-09-21 11:26:13 +0900219// Verify that posting tasks after the pool was destroyed fails but doesn't
220// crash.
221TEST_P(TaskSchedulerWorkerPoolTest, PostAfterDestroy) {
222 StartWorkerPool();
223 auto task_runner = test::CreateTaskRunnerWithExecutionMode(
224 worker_pool_.get(), GetParam().execution_mode);
Peter Kasting24efe5e2018-02-24 09:03:01 +0900225 EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
Gabriel Charettec37bf442017-09-21 11:26:13 +0900226 task_tracker_.Shutdown();
227 worker_pool_->JoinForTesting();
228 worker_pool_.reset();
229 EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
230}
231
Jeffrey He68d29bc2017-08-24 02:14:16 +0900232// Verify that a Task runs shortly after its delay expires.
233TEST_P(TaskSchedulerWorkerPoolTest, PostDelayedTask) {
234 StartWorkerPool();
235 TimeTicks start_time = TimeTicks::Now();
236
237 // Post a task with a short delay.
238 WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
239 WaitableEvent::InitialState::NOT_SIGNALED);
240 EXPECT_TRUE(test::CreateTaskRunnerWithExecutionMode(worker_pool_.get(),
241 GetParam().execution_mode)
242 ->PostDelayedTask(
243 FROM_HERE,
244 BindOnce(&WaitableEvent::Signal, Unretained(&task_ran)),
245 TestTimeouts::tiny_timeout()));
246
247 // Wait until the task runs.
248 task_ran.Wait();
249
250 // Expect the task to run after its delay expires, but not more than 250 ms
251 // after that.
252 const TimeDelta actual_delay = TimeTicks::Now() - start_time;
253 EXPECT_GE(actual_delay, TestTimeouts::tiny_timeout());
254 EXPECT_LT(actual_delay,
255 TimeDelta::FromMilliseconds(250) + TestTimeouts::tiny_timeout());
256}
257
258// Verify that the RunsTasksInCurrentSequence() method of a SEQUENCED TaskRunner
259// returns false when called from a task that isn't part of the sequence. Note:
260// Tests that use TestTaskFactory already verify that
261// RunsTasksInCurrentSequence() returns true when appropriate so this method
262// complements it to get full coverage of that method.
263TEST_P(TaskSchedulerWorkerPoolTest, SequencedRunsTasksInCurrentSequence) {
264 StartWorkerPool();
265 auto task_runner = test::CreateTaskRunnerWithExecutionMode(
266 worker_pool_.get(), GetParam().execution_mode);
267 auto sequenced_task_runner =
268 worker_pool_->CreateSequencedTaskRunnerWithTraits(TaskTraits());
269
270 WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
271 WaitableEvent::InitialState::NOT_SIGNALED);
272 task_runner->PostTask(
273 FROM_HERE,
274 BindOnce(
275 [](scoped_refptr<TaskRunner> sequenced_task_runner,
276 WaitableEvent* task_ran) {
277 EXPECT_FALSE(sequenced_task_runner->RunsTasksInCurrentSequence());
278 task_ran->Signal();
279 },
280 sequenced_task_runner, Unretained(&task_ran)));
281 task_ran.Wait();
282}
283
Gabriel Charettec37bf442017-09-21 11:26:13 +0900284// Verify that tasks posted before Start run after Start.
Jeffrey He68d29bc2017-08-24 02:14:16 +0900285TEST_P(TaskSchedulerWorkerPoolTest, PostBeforeStart) {
Francois Doray5f643432017-10-12 01:27:12 +0900286 WaitableEvent task_1_running(WaitableEvent::ResetPolicy::MANUAL,
287 WaitableEvent::InitialState::NOT_SIGNALED);
288 WaitableEvent task_2_running(WaitableEvent::ResetPolicy::MANUAL,
289 WaitableEvent::InitialState::NOT_SIGNALED);
Jeffrey He68d29bc2017-08-24 02:14:16 +0900290
291 scoped_refptr<TaskRunner> task_runner =
292 worker_pool_->CreateTaskRunnerWithTraits({WithBaseSyncPrimitives()});
293
Francois Doray5f643432017-10-12 01:27:12 +0900294 task_runner->PostTask(
295 FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_1_running)));
296 task_runner->PostTask(
297 FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&task_2_running)));
Jeffrey He68d29bc2017-08-24 02:14:16 +0900298
299 // Workers should not be created and tasks should not run before the pool is
300 // started. The sleep is to give time for the tasks to potentially run.
301 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
Francois Doray5f643432017-10-12 01:27:12 +0900302 EXPECT_FALSE(task_1_running.IsSignaled());
303 EXPECT_FALSE(task_2_running.IsSignaled());
Jeffrey He68d29bc2017-08-24 02:14:16 +0900304
305 StartWorkerPool();
306
Francois Doray5f643432017-10-12 01:27:12 +0900307 // Tasks should run shortly after the pool is started.
308 task_1_running.Wait();
309 task_2_running.Wait();
Jeffrey He68d29bc2017-08-24 02:14:16 +0900310
Robert Liaoe25acff2018-01-25 07:39:17 +0900311 task_tracker_.FlushForTesting();
Jeffrey He68d29bc2017-08-24 02:14:16 +0900312}
313
314INSTANTIATE_TEST_CASE_P(GenericParallel,
315 TaskSchedulerWorkerPoolTest,
316 ::testing::Values(PoolExecutionType{
317 PoolType::GENERIC, test::ExecutionMode::PARALLEL}));
318INSTANTIATE_TEST_CASE_P(GenericSequenced,
319 TaskSchedulerWorkerPoolTest,
320 ::testing::Values(PoolExecutionType{
321 PoolType::GENERIC,
322 test::ExecutionMode::SEQUENCED}));
323
Jeffrey He71518662017-08-25 04:54:13 +0900324#if defined(OS_WIN)
325INSTANTIATE_TEST_CASE_P(WinParallel,
326 TaskSchedulerWorkerPoolTest,
327 ::testing::Values(PoolExecutionType{
328 PoolType::WINDOWS, test::ExecutionMode::PARALLEL}));
329INSTANTIATE_TEST_CASE_P(WinSequenced,
330 TaskSchedulerWorkerPoolTest,
331 ::testing::Values(PoolExecutionType{
332 PoolType::WINDOWS,
333 test::ExecutionMode::SEQUENCED}));
334#endif
335
Jeffrey He68d29bc2017-08-24 02:14:16 +0900336} // namespace internal
337} // namespace base