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/task_scheduler_impl.h"
6
7 #include <stddef.h>
8
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/callback.h"
16 #include "base/cfi_buildflags.h"
17 #include "base/debug/stack_trace.h"
18 #include "base/macros.h"
19 #include "base/memory/ptr_util.h"
20 #include "base/metrics/field_trial.h"
21 #include "base/metrics/field_trial_params.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/task_scheduler/environment_config.h"
24 #include "base/task_scheduler/scheduler_worker_observer.h"
25 #include "base/task_scheduler/scheduler_worker_pool_params.h"
26 #include "base/task_scheduler/task_traits.h"
27 #include "base/task_scheduler/test_task_factory.h"
28 #include "base/task_scheduler/test_utils.h"
29 #include "base/test/gtest_util.h"
30 #include "base/test/test_timeouts.h"
31 #include "base/threading/platform_thread.h"
32 #include "base/threading/sequence_local_storage_slot.h"
33 #include "base/threading/simple_thread.h"
34 #include "base/threading/thread.h"
35 #include "base/threading/thread_restrictions.h"
36 #include "base/time/time.h"
37 #include "build/build_config.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39
40 #if defined(OS_POSIX)
41 #include <unistd.h>
42
43 #include "base/debug/leak_annotations.h"
44 #include "base/files/file_descriptor_watcher_posix.h"
45 #include "base/files/file_util.h"
46 #include "base/posix/eintr_wrapper.h"
47 #endif // defined(OS_POSIX)
48
49 #if defined(OS_WIN)
50 #include "base/win/com_init_util.h"
51 #endif // defined(OS_WIN)
52
53 namespace base {
54 namespace internal {
55
56 namespace {
57
58 struct TraitsExecutionModePair {
TraitsExecutionModePairbase::internal::__anon315c264a0111::TraitsExecutionModePair59 TraitsExecutionModePair(const TaskTraits& traits,
60 test::ExecutionMode execution_mode)
61 : traits(traits), execution_mode(execution_mode) {}
62
63 TaskTraits traits;
64 test::ExecutionMode execution_mode;
65 };
66
67 #if DCHECK_IS_ON()
68 // Returns whether I/O calls are allowed on the current thread.
GetIOAllowed()69 bool GetIOAllowed() {
70 const bool previous_value = ThreadRestrictions::SetIOAllowed(true);
71 ThreadRestrictions::SetIOAllowed(previous_value);
72 return previous_value;
73 }
74 #endif
75
76 // Verify that the current thread priority and I/O restrictions are appropriate
77 // to run a Task with |traits|.
78 // Note: ExecutionMode is verified inside TestTaskFactory.
VerifyTaskEnvironment(const TaskTraits & traits)79 void VerifyTaskEnvironment(const TaskTraits& traits) {
80 EXPECT_EQ(CanUseBackgroundPriorityForSchedulerWorker() &&
81 traits.priority() == TaskPriority::BACKGROUND
82 ? ThreadPriority::BACKGROUND
83 : ThreadPriority::NORMAL,
84 PlatformThread::GetCurrentThreadPriority());
85
86 #if DCHECK_IS_ON()
87 // The #if above is required because GetIOAllowed() always returns true when
88 // !DCHECK_IS_ON(), even when |traits| don't allow file I/O.
89 EXPECT_EQ(traits.may_block(), GetIOAllowed());
90 #endif
91
92 // Verify that the thread the task is running on is named as expected.
93 const std::string current_thread_name(PlatformThread::GetName());
94 EXPECT_NE(std::string::npos, current_thread_name.find("TaskScheduler"));
95
96 if (current_thread_name.find("SingleThread") != std::string::npos) {
97 // For now, single-threaded background tasks run on their own threads.
98 // TODO(fdoray): Run single-threaded background tasks on foreground workers
99 // on platforms that don't support background thread priority.
100 EXPECT_NE(
101 std::string::npos,
102 current_thread_name.find(traits.priority() == TaskPriority::BACKGROUND
103 ? "Background"
104 : "Foreground"));
105 } else {
106 EXPECT_NE(std::string::npos,
107 current_thread_name.find(
108 CanUseBackgroundPriorityForSchedulerWorker() &&
109 traits.priority() == TaskPriority::BACKGROUND
110 ? "Background"
111 : "Foreground"));
112 }
113 EXPECT_EQ(traits.may_block(),
114 current_thread_name.find("Blocking") != std::string::npos);
115 }
116
VerifyTaskEnvironmentAndSignalEvent(const TaskTraits & traits,WaitableEvent * event)117 void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
118 WaitableEvent* event) {
119 DCHECK(event);
120 VerifyTaskEnvironment(traits);
121 event->Signal();
122 }
123
VerifyTimeAndTaskEnvironmentAndSignalEvent(const TaskTraits & traits,TimeTicks expected_time,WaitableEvent * event)124 void VerifyTimeAndTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
125 TimeTicks expected_time,
126 WaitableEvent* event) {
127 DCHECK(event);
128 EXPECT_LE(expected_time, TimeTicks::Now());
129 VerifyTaskEnvironment(traits);
130 event->Signal();
131 }
132
CreateTaskRunnerWithTraitsAndExecutionMode(TaskScheduler * scheduler,const TaskTraits & traits,test::ExecutionMode execution_mode,SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode=SingleThreadTaskRunnerThreadMode::SHARED)133 scoped_refptr<TaskRunner> CreateTaskRunnerWithTraitsAndExecutionMode(
134 TaskScheduler* scheduler,
135 const TaskTraits& traits,
136 test::ExecutionMode execution_mode,
137 SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
138 SingleThreadTaskRunnerThreadMode::SHARED) {
139 switch (execution_mode) {
140 case test::ExecutionMode::PARALLEL:
141 return scheduler->CreateTaskRunnerWithTraits(traits);
142 case test::ExecutionMode::SEQUENCED:
143 return scheduler->CreateSequencedTaskRunnerWithTraits(traits);
144 case test::ExecutionMode::SINGLE_THREADED: {
145 return scheduler->CreateSingleThreadTaskRunnerWithTraits(
146 traits, default_single_thread_task_runner_mode);
147 }
148 }
149 ADD_FAILURE() << "Unknown ExecutionMode";
150 return nullptr;
151 }
152
153 class ThreadPostingTasks : public SimpleThread {
154 public:
155 // Creates a thread that posts Tasks to |scheduler| with |traits| and
156 // |execution_mode|.
ThreadPostingTasks(TaskSchedulerImpl * scheduler,const TaskTraits & traits,test::ExecutionMode execution_mode)157 ThreadPostingTasks(TaskSchedulerImpl* scheduler,
158 const TaskTraits& traits,
159 test::ExecutionMode execution_mode)
160 : SimpleThread("ThreadPostingTasks"),
161 traits_(traits),
162 factory_(CreateTaskRunnerWithTraitsAndExecutionMode(scheduler,
163 traits,
164 execution_mode),
165 execution_mode) {}
166
WaitForAllTasksToRun()167 void WaitForAllTasksToRun() { factory_.WaitForAllTasksToRun(); }
168
169 private:
Run()170 void Run() override {
171 EXPECT_FALSE(factory_.task_runner()->RunsTasksInCurrentSequence());
172
173 const size_t kNumTasksPerThread = 150;
174 for (size_t i = 0; i < kNumTasksPerThread; ++i) {
175 factory_.PostTask(test::TestTaskFactory::PostNestedTask::NO,
176 Bind(&VerifyTaskEnvironment, traits_));
177 }
178 }
179
180 const TaskTraits traits_;
181 test::TestTaskFactory factory_;
182
183 DISALLOW_COPY_AND_ASSIGN(ThreadPostingTasks);
184 };
185
186 // Returns a vector with a TraitsExecutionModePair for each valid
187 // combination of {ExecutionMode, TaskPriority, MayBlock()}.
GetTraitsExecutionModePairs()188 std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairs() {
189 std::vector<TraitsExecutionModePair> params;
190
191 const test::ExecutionMode execution_modes[] = {
192 test::ExecutionMode::PARALLEL, test::ExecutionMode::SEQUENCED,
193 test::ExecutionMode::SINGLE_THREADED};
194
195 for (test::ExecutionMode execution_mode : execution_modes) {
196 for (size_t priority_index = static_cast<size_t>(TaskPriority::LOWEST);
197 priority_index <= static_cast<size_t>(TaskPriority::HIGHEST);
198 ++priority_index) {
199 const TaskPriority priority = static_cast<TaskPriority>(priority_index);
200 params.push_back(TraitsExecutionModePair({priority}, execution_mode));
201 params.push_back(TraitsExecutionModePair({MayBlock()}, execution_mode));
202 }
203 }
204
205 return params;
206 }
207
208 class TaskSchedulerImplTest
209 : public testing::TestWithParam<TraitsExecutionModePair> {
210 protected:
TaskSchedulerImplTest()211 TaskSchedulerImplTest() : scheduler_("Test"), field_trial_list_(nullptr) {}
212
EnableAllTasksUserBlocking()213 void EnableAllTasksUserBlocking() {
214 constexpr char kFieldTrialName[] = "BrowserScheduler";
215 constexpr char kFieldTrialTestGroup[] = "DummyGroup";
216 std::map<std::string, std::string> variation_params;
217 variation_params["AllTasksUserBlocking"] = "true";
218 base::AssociateFieldTrialParams(kFieldTrialName, kFieldTrialTestGroup,
219 variation_params);
220 base::FieldTrialList::CreateFieldTrial(kFieldTrialName,
221 kFieldTrialTestGroup);
222 }
223
set_scheduler_worker_observer(SchedulerWorkerObserver * scheduler_worker_observer)224 void set_scheduler_worker_observer(
225 SchedulerWorkerObserver* scheduler_worker_observer) {
226 scheduler_worker_observer_ = scheduler_worker_observer;
227 }
228
StartTaskScheduler()229 void StartTaskScheduler() {
230 constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
231 constexpr int kMaxNumBackgroundThreads = 1;
232 constexpr int kMaxNumBackgroundBlockingThreads = 3;
233 constexpr int kMaxNumForegroundThreads = 4;
234 constexpr int kMaxNumForegroundBlockingThreads = 12;
235
236 scheduler_.Start(
237 {{kMaxNumBackgroundThreads, kSuggestedReclaimTime},
238 {kMaxNumBackgroundBlockingThreads, kSuggestedReclaimTime},
239 {kMaxNumForegroundThreads, kSuggestedReclaimTime},
240 {kMaxNumForegroundBlockingThreads, kSuggestedReclaimTime}},
241 scheduler_worker_observer_);
242 }
243
TearDown()244 void TearDown() override {
245 if (did_tear_down_)
246 return;
247
248 scheduler_.FlushForTesting();
249 scheduler_.JoinForTesting();
250 did_tear_down_ = true;
251 }
252
253 TaskSchedulerImpl scheduler_;
254
255 private:
256 base::FieldTrialList field_trial_list_;
257 SchedulerWorkerObserver* scheduler_worker_observer_ = nullptr;
258 bool did_tear_down_ = false;
259
260 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImplTest);
261 };
262
263 } // namespace
264
265 // Verifies that a Task posted via PostDelayedTaskWithTraits with parameterized
266 // TaskTraits and no delay runs on a thread with the expected priority and I/O
267 // restrictions. The ExecutionMode parameter is ignored by this test.
TEST_P(TaskSchedulerImplTest,PostDelayedTaskWithTraitsNoDelay)268 TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsNoDelay) {
269 StartTaskScheduler();
270 WaitableEvent task_ran;
271 scheduler_.PostDelayedTaskWithTraits(
272 FROM_HERE, GetParam().traits,
273 BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
274 Unretained(&task_ran)),
275 TimeDelta());
276 task_ran.Wait();
277 }
278
279 // Verifies that a Task posted via PostDelayedTaskWithTraits with parameterized
280 // TaskTraits and a non-zero delay runs on a thread with the expected priority
281 // and I/O restrictions after the delay expires. The ExecutionMode parameter is
282 // ignored by this test.
TEST_P(TaskSchedulerImplTest,PostDelayedTaskWithTraitsWithDelay)283 TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsWithDelay) {
284 StartTaskScheduler();
285 WaitableEvent task_ran;
286 scheduler_.PostDelayedTaskWithTraits(
287 FROM_HERE, GetParam().traits,
288 BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
289 TimeTicks::Now() + TestTimeouts::tiny_timeout(),
290 Unretained(&task_ran)),
291 TestTimeouts::tiny_timeout());
292 task_ran.Wait();
293 }
294
295 // Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and
296 // ExecutionMode run on a thread with the expected priority and I/O restrictions
297 // and respect the characteristics of their ExecutionMode.
TEST_P(TaskSchedulerImplTest,PostTasksViaTaskRunner)298 TEST_P(TaskSchedulerImplTest, PostTasksViaTaskRunner) {
299 StartTaskScheduler();
300 test::TestTaskFactory factory(
301 CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
302 GetParam().execution_mode),
303 GetParam().execution_mode);
304 EXPECT_FALSE(factory.task_runner()->RunsTasksInCurrentSequence());
305
306 const size_t kNumTasksPerTest = 150;
307 for (size_t i = 0; i < kNumTasksPerTest; ++i) {
308 factory.PostTask(test::TestTaskFactory::PostNestedTask::NO,
309 Bind(&VerifyTaskEnvironment, GetParam().traits));
310 }
311
312 factory.WaitForAllTasksToRun();
313 }
314
315 // Verifies that a task posted via PostDelayedTaskWithTraits without a delay
316 // doesn't run before Start() is called.
TEST_P(TaskSchedulerImplTest,PostDelayedTaskWithTraitsNoDelayBeforeStart)317 TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsNoDelayBeforeStart) {
318 WaitableEvent task_running;
319 scheduler_.PostDelayedTaskWithTraits(
320 FROM_HERE, GetParam().traits,
321 BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetParam().traits,
322 Unretained(&task_running)),
323 TimeDelta());
324
325 // Wait a little bit to make sure that the task doesn't run before Start().
326 // Note: This test won't catch a case where the task runs just after the check
327 // and before Start(). However, we expect the test to be flaky if the tested
328 // code allows that to happen.
329 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
330 EXPECT_FALSE(task_running.IsSignaled());
331
332 StartTaskScheduler();
333 task_running.Wait();
334 }
335
336 // Verifies that a task posted via PostDelayedTaskWithTraits with a delay
337 // doesn't run before Start() is called.
TEST_P(TaskSchedulerImplTest,PostDelayedTaskWithTraitsWithDelayBeforeStart)338 TEST_P(TaskSchedulerImplTest, PostDelayedTaskWithTraitsWithDelayBeforeStart) {
339 WaitableEvent task_running;
340 scheduler_.PostDelayedTaskWithTraits(
341 FROM_HERE, GetParam().traits,
342 BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetParam().traits,
343 TimeTicks::Now() + TestTimeouts::tiny_timeout(),
344 Unretained(&task_running)),
345 TestTimeouts::tiny_timeout());
346
347 // Wait a little bit to make sure that the task doesn't run before Start().
348 // Note: This test won't catch a case where the task runs just after the check
349 // and before Start(). However, we expect the test to be flaky if the tested
350 // code allows that to happen.
351 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
352 EXPECT_FALSE(task_running.IsSignaled());
353
354 StartTaskScheduler();
355 task_running.Wait();
356 }
357
358 // Verifies that a task posted via a TaskRunner doesn't run before Start() is
359 // called.
TEST_P(TaskSchedulerImplTest,PostTaskViaTaskRunnerBeforeStart)360 TEST_P(TaskSchedulerImplTest, PostTaskViaTaskRunnerBeforeStart) {
361 WaitableEvent task_running;
362 CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
363 GetParam().execution_mode)
364 ->PostTask(FROM_HERE,
365 BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
366 GetParam().traits, Unretained(&task_running)));
367
368 // Wait a little bit to make sure that the task doesn't run before Start().
369 // Note: This test won't catch a case where the task runs just after the check
370 // and before Start(). However, we expect the test to be flaky if the tested
371 // code allows that to happen.
372 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
373 EXPECT_FALSE(task_running.IsSignaled());
374
375 StartTaskScheduler();
376
377 // This should not hang if the task runs after Start().
378 task_running.Wait();
379 }
380
381 // Verify that all tasks posted to a TaskRunner after Start() run in a
382 // USER_BLOCKING environment when the AllTasksUserBlocking variation param of
383 // the BrowserScheduler experiment is true.
TEST_P(TaskSchedulerImplTest,AllTasksAreUserBlockingTaskRunner)384 TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlockingTaskRunner) {
385 EnableAllTasksUserBlocking();
386 StartTaskScheduler();
387
388 WaitableEvent task_running;
389 CreateTaskRunnerWithTraitsAndExecutionMode(&scheduler_, GetParam().traits,
390 GetParam().execution_mode)
391 ->PostTask(FROM_HERE,
392 BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
393 TaskTraits::Override(GetParam().traits,
394 {TaskPriority::USER_BLOCKING}),
395 Unretained(&task_running)));
396 task_running.Wait();
397 }
398
399 // Verify that all tasks posted via PostDelayedTaskWithTraits() after Start()
400 // run in a USER_BLOCKING environment when the AllTasksUserBlocking variation
401 // param of the BrowserScheduler experiment is true.
TEST_P(TaskSchedulerImplTest,AllTasksAreUserBlocking)402 TEST_P(TaskSchedulerImplTest, AllTasksAreUserBlocking) {
403 EnableAllTasksUserBlocking();
404 StartTaskScheduler();
405
406 WaitableEvent task_running;
407 // Ignore |params.execution_mode| in this test.
408 scheduler_.PostDelayedTaskWithTraits(
409 FROM_HERE, GetParam().traits,
410 BindOnce(&VerifyTaskEnvironmentAndSignalEvent,
411 TaskTraits::Override(GetParam().traits,
412 {TaskPriority::USER_BLOCKING}),
413 Unretained(&task_running)),
414 TimeDelta());
415 task_running.Wait();
416 }
417
418 // Verifies that FlushAsyncForTesting() calls back correctly for all trait and
419 // execution mode pairs.
TEST_P(TaskSchedulerImplTest,FlushAsyncForTestingSimple)420 TEST_P(TaskSchedulerImplTest, FlushAsyncForTestingSimple) {
421 StartTaskScheduler();
422
423 WaitableEvent unblock_task;
424 CreateTaskRunnerWithTraitsAndExecutionMode(
425 &scheduler_,
426 TaskTraits::Override(GetParam().traits, {WithBaseSyncPrimitives()}),
427 GetParam().execution_mode, SingleThreadTaskRunnerThreadMode::DEDICATED)
428 ->PostTask(FROM_HERE,
429 BindOnce(&WaitableEvent::Wait, Unretained(&unblock_task)));
430
431 WaitableEvent flush_event;
432 scheduler_.FlushAsyncForTesting(
433 BindOnce(&WaitableEvent::Signal, Unretained(&flush_event)));
434 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
435 EXPECT_FALSE(flush_event.IsSignaled());
436
437 unblock_task.Signal();
438
439 flush_event.Wait();
440 }
441
442 INSTANTIATE_TEST_CASE_P(OneTraitsExecutionModePair,
443 TaskSchedulerImplTest,
444 ::testing::ValuesIn(GetTraitsExecutionModePairs()));
445
446 // Spawns threads that simultaneously post Tasks to TaskRunners with various
447 // TaskTraits and ExecutionModes. Verifies that each Task runs on a thread with
448 // the expected priority and I/O restrictions and respects the characteristics
449 // of its ExecutionMode.
TEST_F(TaskSchedulerImplTest,MultipleTraitsExecutionModePairs)450 TEST_F(TaskSchedulerImplTest, MultipleTraitsExecutionModePairs) {
451 StartTaskScheduler();
452 std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
453 for (const auto& traits_execution_mode_pair : GetTraitsExecutionModePairs()) {
454 threads_posting_tasks.push_back(WrapUnique(
455 new ThreadPostingTasks(&scheduler_, traits_execution_mode_pair.traits,
456 traits_execution_mode_pair.execution_mode)));
457 threads_posting_tasks.back()->Start();
458 }
459
460 for (const auto& thread : threads_posting_tasks) {
461 thread->WaitForAllTasksToRun();
462 thread->Join();
463 }
464 }
465
TEST_F(TaskSchedulerImplTest,GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated)466 TEST_F(TaskSchedulerImplTest,
467 GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) {
468 StartTaskScheduler();
469
470 // GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated() does not support
471 // TaskPriority::BACKGROUND.
472 EXPECT_DCHECK_DEATH({
473 scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
474 {TaskPriority::BACKGROUND});
475 });
476 EXPECT_DCHECK_DEATH({
477 scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
478 {MayBlock(), TaskPriority::BACKGROUND});
479 });
480
481 EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
482 {TaskPriority::USER_VISIBLE}));
483 EXPECT_EQ(12, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
484 {MayBlock(), TaskPriority::USER_VISIBLE}));
485 EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
486 {TaskPriority::USER_BLOCKING}));
487 EXPECT_EQ(12, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
488 {MayBlock(), TaskPriority::USER_BLOCKING}));
489 }
490
491 // Verify that the RunsTasksInCurrentSequence() method of a SequencedTaskRunner
492 // returns false when called from a task that isn't part of the sequence.
TEST_F(TaskSchedulerImplTest,SequencedRunsTasksInCurrentSequence)493 TEST_F(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) {
494 StartTaskScheduler();
495 auto single_thread_task_runner =
496 scheduler_.CreateSingleThreadTaskRunnerWithTraits(
497 TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
498 auto sequenced_task_runner =
499 scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
500
501 WaitableEvent task_ran;
502 single_thread_task_runner->PostTask(
503 FROM_HERE,
504 BindOnce(
505 [](scoped_refptr<TaskRunner> sequenced_task_runner,
506 WaitableEvent* task_ran) {
507 EXPECT_FALSE(sequenced_task_runner->RunsTasksInCurrentSequence());
508 task_ran->Signal();
509 },
510 sequenced_task_runner, Unretained(&task_ran)));
511 task_ran.Wait();
512 }
513
514 // Verify that the RunsTasksInCurrentSequence() method of a
515 // SingleThreadTaskRunner returns false when called from a task that isn't part
516 // of the sequence.
TEST_F(TaskSchedulerImplTest,SingleThreadRunsTasksInCurrentSequence)517 TEST_F(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) {
518 StartTaskScheduler();
519 auto sequenced_task_runner =
520 scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
521 auto single_thread_task_runner =
522 scheduler_.CreateSingleThreadTaskRunnerWithTraits(
523 TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
524
525 WaitableEvent task_ran;
526 sequenced_task_runner->PostTask(
527 FROM_HERE,
528 BindOnce(
529 [](scoped_refptr<TaskRunner> single_thread_task_runner,
530 WaitableEvent* task_ran) {
531 EXPECT_FALSE(
532 single_thread_task_runner->RunsTasksInCurrentSequence());
533 task_ran->Signal();
534 },
535 single_thread_task_runner, Unretained(&task_ran)));
536 task_ran.Wait();
537 }
538
539 #if defined(OS_WIN)
TEST_F(TaskSchedulerImplTest,COMSTATaskRunnersRunWithCOMSTA)540 TEST_F(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) {
541 StartTaskScheduler();
542 auto com_sta_task_runner = scheduler_.CreateCOMSTATaskRunnerWithTraits(
543 TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
544
545 WaitableEvent task_ran;
546 com_sta_task_runner->PostTask(
547 FROM_HERE, Bind(
548 [](WaitableEvent* task_ran) {
549 win::AssertComApartmentType(win::ComApartmentType::STA);
550 task_ran->Signal();
551 },
552 Unretained(&task_ran)));
553 task_ran.Wait();
554 }
555 #endif // defined(OS_WIN)
556
TEST_F(TaskSchedulerImplTest,DelayedTasksNotRunAfterShutdown)557 TEST_F(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) {
558 StartTaskScheduler();
559 // As with delayed tasks in general, this is racy. If the task does happen to
560 // run after Shutdown within the timeout, it will fail this test.
561 //
562 // The timeout should be set sufficiently long enough to ensure that the
563 // delayed task did not run. 2x is generally good enough.
564 //
565 // A non-racy way to do this would be to post two sequenced tasks:
566 // 1) Regular Post Task: A WaitableEvent.Wait
567 // 2) Delayed Task: ADD_FAILURE()
568 // and signalling the WaitableEvent after Shutdown() on a different thread
569 // since Shutdown() will block. However, the cost of managing this extra
570 // thread was deemed to be too great for the unlikely race.
571 scheduler_.PostDelayedTaskWithTraits(FROM_HERE, TaskTraits(),
572 BindOnce([]() { ADD_FAILURE(); }),
573 TestTimeouts::tiny_timeout());
574 scheduler_.Shutdown();
575 PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 2);
576 }
577
578 #if defined(OS_POSIX)
579
TEST_F(TaskSchedulerImplTest,FileDescriptorWatcherNoOpsAfterShutdown)580 TEST_F(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {
581 StartTaskScheduler();
582
583 int pipes[2];
584 ASSERT_EQ(0, pipe(pipes));
585
586 scoped_refptr<TaskRunner> blocking_task_runner =
587 scheduler_.CreateSequencedTaskRunnerWithTraits(
588 {TaskShutdownBehavior::BLOCK_SHUTDOWN});
589 blocking_task_runner->PostTask(
590 FROM_HERE,
591 BindOnce(
592 [](int read_fd) {
593 std::unique_ptr<FileDescriptorWatcher::Controller> controller =
594 FileDescriptorWatcher::WatchReadable(
595 read_fd, BindRepeating([]() { NOTREACHED(); }));
596
597 // This test is for components that intentionally leak their
598 // watchers at shutdown. We can't clean |controller| up because its
599 // destructor will assert that it's being called from the correct
600 // sequence. After the task scheduler is shutdown, it is not
601 // possible to run tasks on this sequence.
602 //
603 // Note: Do not inline the controller.release() call into the
604 // ANNOTATE_LEAKING_OBJECT_PTR as the annotation is removed
605 // by the preprocessor in non-LEAK_SANITIZER builds,
606 // effectively breaking this test.
607 ANNOTATE_LEAKING_OBJECT_PTR(controller.get());
608 controller.release();
609 },
610 pipes[0]));
611
612 scheduler_.Shutdown();
613
614 constexpr char kByte = '!';
615 ASSERT_TRUE(WriteFileDescriptor(pipes[1], &kByte, sizeof(kByte)));
616
617 // Give a chance for the file watcher to fire before closing the handles.
618 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
619
620 EXPECT_EQ(0, IGNORE_EINTR(close(pipes[0])));
621 EXPECT_EQ(0, IGNORE_EINTR(close(pipes[1])));
622 }
623 #endif // defined(OS_POSIX)
624
625 // Verify that tasks posted on the same sequence access the same values on
626 // SequenceLocalStorage, and tasks on different sequences see different values.
TEST_F(TaskSchedulerImplTest,SequenceLocalStorage)627 TEST_F(TaskSchedulerImplTest, SequenceLocalStorage) {
628 StartTaskScheduler();
629
630 SequenceLocalStorageSlot<int> slot;
631 auto sequenced_task_runner1 =
632 scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
633 auto sequenced_task_runner2 =
634 scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
635
636 sequenced_task_runner1->PostTask(
637 FROM_HERE,
638 BindOnce([](SequenceLocalStorageSlot<int>* slot) { slot->Set(11); },
639 &slot));
640
641 sequenced_task_runner1->PostTask(FROM_HERE,
642 BindOnce(
643 [](SequenceLocalStorageSlot<int>* slot) {
644 EXPECT_EQ(slot->Get(), 11);
645 },
646 &slot));
647
648 sequenced_task_runner2->PostTask(FROM_HERE,
649 BindOnce(
650 [](SequenceLocalStorageSlot<int>* slot) {
651 EXPECT_NE(slot->Get(), 11);
652 },
653 &slot));
654
655 scheduler_.FlushForTesting();
656 }
657
TEST_F(TaskSchedulerImplTest,FlushAsyncNoTasks)658 TEST_F(TaskSchedulerImplTest, FlushAsyncNoTasks) {
659 StartTaskScheduler();
660 bool called_back = false;
661 scheduler_.FlushAsyncForTesting(
662 BindOnce([](bool* called_back) { *called_back = true; },
663 Unretained(&called_back)));
664 EXPECT_TRUE(called_back);
665 }
666
667 namespace {
668
669 // Verifies that |query| is found on the current stack. Ignores failures if this
670 // configuration doesn't have symbols.
VerifyHasStringOnStack(const std::string & query)671 void VerifyHasStringOnStack(const std::string& query) {
672 const std::string stack = debug::StackTrace().ToString();
673 SCOPED_TRACE(stack);
674 const bool found_on_stack = stack.find(query) != std::string::npos;
675 const bool stack_has_symbols =
676 stack.find("SchedulerWorker") != std::string::npos;
677 EXPECT_TRUE(found_on_stack || !stack_has_symbols) << query;
678 }
679
680 } // namespace
681
682 #if defined(OS_POSIX)
683 // Many POSIX bots flakily crash on |debug::StackTrace().ToString()|,
684 // https://crbug.com/840429.
685 #define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
686 #elif defined(OS_WIN) && \
687 (defined(ADDRESS_SANITIZER) || BUILDFLAG(CFI_CAST_CHECK))
688 // Hangs on WinASan and WinCFI (grabbing StackTrace() too slow?),
689 // https://crbug.com/845010#c7.
690 #define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
691 #else
692 #define MAYBE_IdentifiableStacks IdentifiableStacks
693 #endif
694
695 // Integration test that verifies that workers have a frame on their stacks
696 // which easily identifies the type of worker (useful to diagnose issues from
697 // logs without memory dumps).
TEST_F(TaskSchedulerImplTest,MAYBE_IdentifiableStacks)698 TEST_F(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) {
699 StartTaskScheduler();
700
701 scheduler_.CreateSequencedTaskRunnerWithTraits({})->PostTask(
702 FROM_HERE, BindOnce(&VerifyHasStringOnStack, "RunPooledWorker"));
703 scheduler_.CreateSequencedTaskRunnerWithTraits({TaskPriority::BACKGROUND})
704 ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
705 "RunBackgroundPooledWorker"));
706
707 scheduler_
708 .CreateSingleThreadTaskRunnerWithTraits(
709 {}, SingleThreadTaskRunnerThreadMode::SHARED)
710 ->PostTask(FROM_HERE,
711 BindOnce(&VerifyHasStringOnStack, "RunSharedWorker"));
712 scheduler_
713 .CreateSingleThreadTaskRunnerWithTraits(
714 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED)
715 ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
716 "RunBackgroundSharedWorker"));
717
718 scheduler_
719 .CreateSingleThreadTaskRunnerWithTraits(
720 {}, SingleThreadTaskRunnerThreadMode::DEDICATED)
721 ->PostTask(FROM_HERE,
722 BindOnce(&VerifyHasStringOnStack, "RunDedicatedWorker"));
723 scheduler_
724 .CreateSingleThreadTaskRunnerWithTraits(
725 {TaskPriority::BACKGROUND},
726 SingleThreadTaskRunnerThreadMode::DEDICATED)
727 ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
728 "RunBackgroundDedicatedWorker"));
729
730 #if defined(OS_WIN)
731 scheduler_
732 .CreateCOMSTATaskRunnerWithTraits(
733 {}, SingleThreadTaskRunnerThreadMode::SHARED)
734 ->PostTask(FROM_HERE,
735 BindOnce(&VerifyHasStringOnStack, "RunSharedCOMWorker"));
736 scheduler_
737 .CreateCOMSTATaskRunnerWithTraits(
738 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED)
739 ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
740 "RunBackgroundSharedCOMWorker"));
741
742 scheduler_
743 .CreateCOMSTATaskRunnerWithTraits(
744 {}, SingleThreadTaskRunnerThreadMode::DEDICATED)
745 ->PostTask(FROM_HERE,
746 BindOnce(&VerifyHasStringOnStack, "RunDedicatedCOMWorker"));
747 scheduler_
748 .CreateCOMSTATaskRunnerWithTraits(
749 {TaskPriority::BACKGROUND},
750 SingleThreadTaskRunnerThreadMode::DEDICATED)
751 ->PostTask(FROM_HERE, BindOnce(&VerifyHasStringOnStack,
752 "RunBackgroundDedicatedCOMWorker"));
753 #endif // defined(OS_WIN)
754
755 scheduler_.FlushForTesting();
756 }
757
TEST_F(TaskSchedulerImplTest,SchedulerWorkerObserver)758 TEST_F(TaskSchedulerImplTest, SchedulerWorkerObserver) {
759 testing::StrictMock<test::MockSchedulerWorkerObserver> observer;
760 set_scheduler_worker_observer(&observer);
761
762 // A worker should be created for each pool. After that, 8 threads should be
763 // created for single-threaded work (16 on Windows).
764 const int kExpectedNumPoolWorkers =
765 CanUseBackgroundPriorityForSchedulerWorker() ? 4 : 2;
766 #if defined(OS_WIN)
767 const int kExpectedNumSingleThreadedWorkers = 16;
768 #else
769 const int kExpectedNumSingleThreadedWorkers = 8;
770 #endif
771 const int kExpectedNumWorkers =
772 kExpectedNumPoolWorkers + kExpectedNumSingleThreadedWorkers;
773
774 EXPECT_CALL(observer, OnSchedulerWorkerMainEntry())
775 .Times(kExpectedNumWorkers);
776
777 StartTaskScheduler();
778
779 std::vector<scoped_refptr<SingleThreadTaskRunner>> task_runners;
780
781 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
782 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED));
783 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
784 {TaskPriority::BACKGROUND, MayBlock()},
785 SingleThreadTaskRunnerThreadMode::SHARED));
786 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
787 {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
788 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
789 {TaskPriority::USER_BLOCKING, MayBlock()},
790 SingleThreadTaskRunnerThreadMode::SHARED));
791
792 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
793 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::DEDICATED));
794 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
795 {TaskPriority::BACKGROUND, MayBlock()},
796 SingleThreadTaskRunnerThreadMode::DEDICATED));
797 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
798 {TaskPriority::USER_BLOCKING},
799 SingleThreadTaskRunnerThreadMode::DEDICATED));
800 task_runners.push_back(scheduler_.CreateSingleThreadTaskRunnerWithTraits(
801 {TaskPriority::USER_BLOCKING, MayBlock()},
802 SingleThreadTaskRunnerThreadMode::DEDICATED));
803
804 #if defined(OS_WIN)
805 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
806 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::SHARED));
807 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
808 {TaskPriority::BACKGROUND, MayBlock()},
809 SingleThreadTaskRunnerThreadMode::SHARED));
810 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
811 {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
812 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
813 {TaskPriority::USER_BLOCKING, MayBlock()},
814 SingleThreadTaskRunnerThreadMode::SHARED));
815
816 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
817 {TaskPriority::BACKGROUND}, SingleThreadTaskRunnerThreadMode::DEDICATED));
818 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
819 {TaskPriority::BACKGROUND, MayBlock()},
820 SingleThreadTaskRunnerThreadMode::DEDICATED));
821 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
822 {TaskPriority::USER_BLOCKING},
823 SingleThreadTaskRunnerThreadMode::DEDICATED));
824 task_runners.push_back(scheduler_.CreateCOMSTATaskRunnerWithTraits(
825 {TaskPriority::USER_BLOCKING, MayBlock()},
826 SingleThreadTaskRunnerThreadMode::DEDICATED));
827 #endif
828
829 for (auto& task_runner : task_runners)
830 task_runner->PostTask(FROM_HERE, DoNothing());
831
832 EXPECT_CALL(observer, OnSchedulerWorkerMainExit()).Times(kExpectedNumWorkers);
833
834 // Allow single-threaded workers to be released.
835 task_runners.clear();
836
837 TearDown();
838 }
839
840 } // namespace internal
841 } // namespace base
842