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/scheduler_worker.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/synchronization/condition_variable.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/task_scheduler/environment_config.h"
20 #include "base/task_scheduler/scheduler_lock.h"
21 #include "base/task_scheduler/scheduler_worker_observer.h"
22 #include "base/task_scheduler/sequence.h"
23 #include "base/task_scheduler/task.h"
24 #include "base/task_scheduler/task_tracker.h"
25 #include "base/task_scheduler/test_utils.h"
26 #include "base/test/test_timeouts.h"
27 #include "base/threading/platform_thread.h"
28 #include "base/threading/simple_thread.h"
29 #include "base/time/time.h"
30 #include "build/build_config.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 #if defined(OS_WIN)
35 #include <objbase.h>
36
37 #include "base/win/com_init_check_hook.h"
38 #endif
39
40 using testing::_;
41 using testing::Mock;
42 using testing::Ne;
43 using testing::StrictMock;
44
45 namespace base {
46 namespace internal {
47 namespace {
48
49 const size_t kNumSequencesPerTest = 150;
50
51 class SchedulerWorkerDefaultDelegate : public SchedulerWorker::Delegate {
52 public:
53 SchedulerWorkerDefaultDelegate() = default;
54
55 // SchedulerWorker::Delegate:
OnCanScheduleSequence(scoped_refptr<Sequence> sequence)56 void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
57 ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
58 }
GetThreadLabel() const59 SchedulerWorker::ThreadLabel GetThreadLabel() const override {
60 return SchedulerWorker::ThreadLabel::DEDICATED;
61 }
OnMainEntry(const SchedulerWorker * worker)62 void OnMainEntry(const SchedulerWorker* worker) override {}
GetWork(SchedulerWorker * worker)63 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
64 return nullptr;
65 }
DidRunTask()66 void DidRunTask() override {
67 ADD_FAILURE() << "Unexpected call to DidRunTask()";
68 }
ReEnqueueSequence(scoped_refptr<Sequence> sequence)69 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
70 ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
71 }
GetSleepTimeout()72 TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
73
74 private:
75 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDefaultDelegate);
76 };
77
78 // The test parameter is the number of Tasks per Sequence returned by GetWork().
79 class TaskSchedulerWorkerTest : public testing::TestWithParam<size_t> {
80 protected:
TaskSchedulerWorkerTest()81 TaskSchedulerWorkerTest()
82 : num_get_work_cv_(lock_.CreateConditionVariable()) {}
83
SetUp()84 void SetUp() override {
85 worker_ = MakeRefCounted<SchedulerWorker>(
86 ThreadPriority::NORMAL,
87 std::make_unique<TestSchedulerWorkerDelegate>(this),
88 task_tracker_.GetTrackedRef());
89 ASSERT_TRUE(worker_);
90 worker_->Start();
91 worker_set_.Signal();
92 main_entry_called_.Wait();
93 }
94
TearDown()95 void TearDown() override {
96 // |worker_| needs to be released before ~TaskTracker() as it holds a
97 // TrackedRef to it.
98 worker_->JoinForTesting();
99 worker_ = nullptr;
100 }
101
TasksPerSequence() const102 size_t TasksPerSequence() const { return GetParam(); }
103
104 // Wait until GetWork() has been called |num_get_work| times.
WaitForNumGetWork(size_t num_get_work)105 void WaitForNumGetWork(size_t num_get_work) {
106 AutoSchedulerLock auto_lock(lock_);
107 while (num_get_work_ < num_get_work)
108 num_get_work_cv_->Wait();
109 }
110
SetMaxGetWork(size_t max_get_work)111 void SetMaxGetWork(size_t max_get_work) {
112 AutoSchedulerLock auto_lock(lock_);
113 max_get_work_ = max_get_work;
114 }
115
SetNumSequencesToCreate(size_t num_sequences_to_create)116 void SetNumSequencesToCreate(size_t num_sequences_to_create) {
117 AutoSchedulerLock auto_lock(lock_);
118 EXPECT_EQ(0U, num_sequences_to_create_);
119 num_sequences_to_create_ = num_sequences_to_create;
120 }
121
NumRunTasks()122 size_t NumRunTasks() {
123 AutoSchedulerLock auto_lock(lock_);
124 return num_run_tasks_;
125 }
126
CreatedSequences()127 std::vector<scoped_refptr<Sequence>> CreatedSequences() {
128 AutoSchedulerLock auto_lock(lock_);
129 return created_sequences_;
130 }
131
EnqueuedSequences()132 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() {
133 AutoSchedulerLock auto_lock(lock_);
134 return re_enqueued_sequences_;
135 }
136
137 scoped_refptr<SchedulerWorker> worker_;
138
139 private:
140 class TestSchedulerWorkerDelegate : public SchedulerWorkerDefaultDelegate {
141 public:
TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest * outer)142 TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest* outer)
143 : outer_(outer) {}
144
~TestSchedulerWorkerDelegate()145 ~TestSchedulerWorkerDelegate() override {
146 EXPECT_FALSE(IsCallToDidRunTaskExpected());
147 }
148
149 // SchedulerWorker::Delegate:
OnMainEntry(const SchedulerWorker * worker)150 void OnMainEntry(const SchedulerWorker* worker) override {
151 outer_->worker_set_.Wait();
152 EXPECT_EQ(outer_->worker_.get(), worker);
153 EXPECT_FALSE(IsCallToDidRunTaskExpected());
154
155 // Without synchronization, OnMainEntry() could be called twice without
156 // generating an error.
157 AutoSchedulerLock auto_lock(outer_->lock_);
158 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled());
159 outer_->main_entry_called_.Signal();
160 }
161
GetWork(SchedulerWorker * worker)162 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
163 EXPECT_FALSE(IsCallToDidRunTaskExpected());
164 EXPECT_EQ(outer_->worker_.get(), worker);
165
166 {
167 AutoSchedulerLock auto_lock(outer_->lock_);
168
169 // Increment the number of times that this method has been called.
170 ++outer_->num_get_work_;
171 outer_->num_get_work_cv_->Signal();
172
173 // Verify that this method isn't called more times than expected.
174 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_);
175
176 // Check if a Sequence should be returned.
177 if (outer_->num_sequences_to_create_ == 0)
178 return nullptr;
179 --outer_->num_sequences_to_create_;
180 }
181
182 // Create a Sequence with TasksPerSequence() Tasks.
183 scoped_refptr<Sequence> sequence(new Sequence);
184 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) {
185 Task task(FROM_HERE,
186 BindOnce(&TaskSchedulerWorkerTest::RunTaskCallback,
187 Unretained(outer_)),
188 TaskTraits(), TimeDelta());
189 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(&task));
190 sequence->PushTask(std::move(task));
191 }
192
193 ExpectCallToDidRunTask();
194
195 {
196 // Add the Sequence to the vector of created Sequences.
197 AutoSchedulerLock auto_lock(outer_->lock_);
198 outer_->created_sequences_.push_back(sequence);
199 }
200
201 sequence = outer_->task_tracker_.WillScheduleSequence(std::move(sequence),
202 nullptr);
203 EXPECT_TRUE(sequence);
204 return sequence;
205 }
206
DidRunTask()207 void DidRunTask() override {
208 AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
209 EXPECT_TRUE(expect_did_run_task_);
210 expect_did_run_task_ = false;
211 }
212
213 // This override verifies that |sequence| contains the expected number of
214 // Tasks and adds it to |enqueued_sequences_|. Unlike a normal
215 // EnqueueSequence implementation, it doesn't reinsert |sequence| into a
216 // queue for further execution.
ReEnqueueSequence(scoped_refptr<Sequence> sequence)217 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
218 EXPECT_FALSE(IsCallToDidRunTaskExpected());
219 EXPECT_GT(outer_->TasksPerSequence(), 1U);
220
221 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks.
222 for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) {
223 EXPECT_TRUE(sequence->TakeTask());
224 EXPECT_EQ(i == outer_->TasksPerSequence() - 2, sequence->Pop());
225 }
226
227 // Add |sequence| to |re_enqueued_sequences_|.
228 AutoSchedulerLock auto_lock(outer_->lock_);
229 outer_->re_enqueued_sequences_.push_back(std::move(sequence));
230 EXPECT_LE(outer_->re_enqueued_sequences_.size(),
231 outer_->created_sequences_.size());
232 }
233
234 private:
235 // Expect a call to DidRunTask() before the next call to any other method of
236 // this delegate.
ExpectCallToDidRunTask()237 void ExpectCallToDidRunTask() {
238 AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
239 expect_did_run_task_ = true;
240 }
241
IsCallToDidRunTaskExpected() const242 bool IsCallToDidRunTaskExpected() const {
243 AutoSchedulerLock auto_lock(expect_did_run_task_lock_);
244 return expect_did_run_task_;
245 }
246
247 TaskSchedulerWorkerTest* outer_;
248
249 // Synchronizes access to |expect_did_run_task_|.
250 mutable SchedulerLock expect_did_run_task_lock_;
251
252 // Whether the next method called on this delegate should be DidRunTask().
253 bool expect_did_run_task_ = false;
254
255 DISALLOW_COPY_AND_ASSIGN(TestSchedulerWorkerDelegate);
256 };
257
RunTaskCallback()258 void RunTaskCallback() {
259 AutoSchedulerLock auto_lock(lock_);
260 ++num_run_tasks_;
261 EXPECT_LE(num_run_tasks_, created_sequences_.size());
262 }
263
264 TaskTracker task_tracker_ = {"Test"};
265
266 // Synchronizes access to all members below.
267 mutable SchedulerLock lock_;
268
269 // Signaled once OnMainEntry() has been called.
270 WaitableEvent main_entry_called_;
271
272 // Number of Sequences that should be created by GetWork(). When this
273 // is 0, GetWork() returns nullptr.
274 size_t num_sequences_to_create_ = 0;
275
276 // Number of times that GetWork() has been called.
277 size_t num_get_work_ = 0;
278
279 // Maximum number of times that GetWork() can be called.
280 size_t max_get_work_ = 0;
281
282 // Condition variable signaled when |num_get_work_| is incremented.
283 std::unique_ptr<ConditionVariable> num_get_work_cv_;
284
285 // Sequences created by GetWork().
286 std::vector<scoped_refptr<Sequence>> created_sequences_;
287
288 // Sequences passed to EnqueueSequence().
289 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_;
290
291 // Number of times that RunTaskCallback() has been called.
292 size_t num_run_tasks_ = 0;
293
294 // Signaled after |worker_| is set.
295 WaitableEvent worker_set_;
296
297 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerTest);
298 };
299
300 } // namespace
301
302 // Verify that when GetWork() continuously returns Sequences, all Tasks in these
303 // Sequences run successfully. The test wakes up the SchedulerWorker once.
TEST_P(TaskSchedulerWorkerTest,ContinuousWork)304 TEST_P(TaskSchedulerWorkerTest, ContinuousWork) {
305 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to
306 // return nullptr.
307 SetNumSequencesToCreate(kNumSequencesPerTest);
308
309 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a
310 // Sequence and one call in which its returns nullptr.
311 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1;
312 SetMaxGetWork(kExpectedNumGetWork);
313
314 // Wake up |worker_| and wait until GetWork() has been invoked the
315 // expected amount of times.
316 worker_->WakeUp();
317 WaitForNumGetWork(kExpectedNumGetWork);
318
319 // All tasks should have run.
320 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks());
321
322 // If Sequences returned by GetWork() contain more than one Task, they aren't
323 // empty after the worker pops Tasks from them and thus should be returned to
324 // EnqueueSequence().
325 if (TasksPerSequence() > 1)
326 EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
327 else
328 EXPECT_TRUE(EnqueuedSequences().empty());
329 }
330
331 // Verify that when GetWork() alternates between returning a Sequence and
332 // returning nullptr, all Tasks in the returned Sequences run successfully. The
333 // test wakes up the SchedulerWorker once for each Sequence.
TEST_P(TaskSchedulerWorkerTest,IntermittentWork)334 TEST_P(TaskSchedulerWorkerTest, IntermittentWork) {
335 for (size_t i = 0; i < kNumSequencesPerTest; ++i) {
336 // Set GetWork() to return 1 Sequence before starting to return
337 // nullptr.
338 SetNumSequencesToCreate(1);
339
340 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and
341 // |i + 1| calls in which it returns nullptr.
342 const size_t expected_num_get_work = 2 * (i + 1);
343 SetMaxGetWork(expected_num_get_work);
344
345 // Wake up |worker_| and wait until GetWork() has been invoked
346 // the expected amount of times.
347 worker_->WakeUp();
348 WaitForNumGetWork(expected_num_get_work);
349
350 // The Task should have run
351 EXPECT_EQ(i + 1, NumRunTasks());
352
353 // If Sequences returned by GetWork() contain more than one Task, they
354 // aren't empty after the worker pops Tasks from them and thus should be
355 // returned to EnqueueSequence().
356 if (TasksPerSequence() > 1)
357 EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
358 else
359 EXPECT_TRUE(EnqueuedSequences().empty());
360 }
361 }
362
363 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence,
364 TaskSchedulerWorkerTest,
365 ::testing::Values(1));
366 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence,
367 TaskSchedulerWorkerTest,
368 ::testing::Values(2));
369
370 namespace {
371
372 class ControllableCleanupDelegate : public SchedulerWorkerDefaultDelegate {
373 public:
374 class Controls : public RefCountedThreadSafe<Controls> {
375 public:
376 Controls() = default;
377
HaveWorkBlock()378 void HaveWorkBlock() { work_running_.Reset(); }
379
UnblockWork()380 void UnblockWork() { work_running_.Signal(); }
381
WaitForWorkToRun()382 void WaitForWorkToRun() { work_processed_.Wait(); }
383
WaitForCleanupRequest()384 void WaitForCleanupRequest() { cleanup_requested_.Wait(); }
385
WaitForDelegateDestroy()386 void WaitForDelegateDestroy() { destroyed_.Wait(); }
387
WaitForMainExit()388 void WaitForMainExit() { exited_.Wait(); }
389
set_expect_get_work(bool expect_get_work)390 void set_expect_get_work(bool expect_get_work) {
391 expect_get_work_ = expect_get_work;
392 }
393
ResetState()394 void ResetState() {
395 work_running_.Signal();
396 work_processed_.Reset();
397 cleanup_requested_.Reset();
398 exited_.Reset();
399 work_requested_ = false;
400 }
401
set_can_cleanup(bool can_cleanup)402 void set_can_cleanup(bool can_cleanup) { can_cleanup_ = can_cleanup; }
403
404 private:
405 friend class ControllableCleanupDelegate;
406 friend class RefCountedThreadSafe<Controls>;
407 ~Controls() = default;
408
409 WaitableEvent work_running_{WaitableEvent::ResetPolicy::MANUAL,
410 WaitableEvent::InitialState::SIGNALED};
411 WaitableEvent work_processed_;
412 WaitableEvent cleanup_requested_;
413 WaitableEvent destroyed_;
414 WaitableEvent exited_;
415
416 bool expect_get_work_ = true;
417 bool can_cleanup_ = false;
418 bool work_requested_ = false;
419
420 DISALLOW_COPY_AND_ASSIGN(Controls);
421 };
422
ControllableCleanupDelegate(TaskTracker * task_tracker)423 ControllableCleanupDelegate(TaskTracker* task_tracker)
424 : task_tracker_(task_tracker), controls_(new Controls()) {}
425
~ControllableCleanupDelegate()426 ~ControllableCleanupDelegate() override { controls_->destroyed_.Signal(); }
427
GetWork(SchedulerWorker * worker)428 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker)
429 override {
430 EXPECT_TRUE(controls_->expect_get_work_);
431
432 // Sends one item of work to signal |work_processed_|. On subsequent calls,
433 // sends nullptr to indicate there's no more work to be done.
434 if (controls_->work_requested_) {
435 if (CanCleanup(worker)) {
436 OnCleanup();
437 worker->Cleanup();
438 controls_->set_expect_get_work(false);
439 }
440 return nullptr;
441 }
442
443 controls_->work_requested_ = true;
444 scoped_refptr<Sequence> sequence(new Sequence);
445 Task task(
446 FROM_HERE,
447 BindOnce(
448 [](WaitableEvent* work_processed, WaitableEvent* work_running) {
449 work_processed->Signal();
450 work_running->Wait();
451 },
452 Unretained(&controls_->work_processed_),
453 Unretained(&controls_->work_running_)),
454 {WithBaseSyncPrimitives(), TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
455 TimeDelta());
456 EXPECT_TRUE(task_tracker_->WillPostTask(&task));
457 sequence->PushTask(std::move(task));
458 sequence =
459 task_tracker_->WillScheduleSequence(std::move(sequence), nullptr);
460 EXPECT_TRUE(sequence);
461 return sequence;
462 }
463
DidRunTask()464 void DidRunTask() override {}
465
OnMainExit(SchedulerWorker * worker)466 void OnMainExit(SchedulerWorker* worker) override {
467 controls_->exited_.Signal();
468 }
469
CanCleanup(SchedulerWorker * worker)470 bool CanCleanup(SchedulerWorker* worker) {
471 // Saving |can_cleanup_| now so that callers waiting on |cleanup_requested_|
472 // have the thread go to sleep and then allow timing out.
473 bool can_cleanup = controls_->can_cleanup_;
474 controls_->cleanup_requested_.Signal();
475 return can_cleanup;
476 }
477
OnCleanup()478 void OnCleanup() {
479 EXPECT_TRUE(controls_->can_cleanup_);
480 EXPECT_TRUE(controls_->cleanup_requested_.IsSignaled());
481 }
482
483 // ControllableCleanupDelegate:
controls()484 scoped_refptr<Controls> controls() { return controls_; }
485
486 private:
487 scoped_refptr<Sequence> work_sequence_;
488 TaskTracker* const task_tracker_;
489 scoped_refptr<Controls> controls_;
490
491 DISALLOW_COPY_AND_ASSIGN(ControllableCleanupDelegate);
492 };
493
494 class MockedControllableCleanupDelegate : public ControllableCleanupDelegate {
495 public:
MockedControllableCleanupDelegate(TaskTracker * task_tracker)496 MockedControllableCleanupDelegate(TaskTracker* task_tracker)
497 : ControllableCleanupDelegate(task_tracker){};
498 ~MockedControllableCleanupDelegate() override = default;
499
500 // SchedulerWorker::Delegate:
501 MOCK_METHOD1(OnMainEntry, void(const SchedulerWorker* worker));
502
503 private:
504 DISALLOW_COPY_AND_ASSIGN(MockedControllableCleanupDelegate);
505 };
506
507 } // namespace
508
509 // Verify that calling SchedulerWorker::Cleanup() from GetWork() causes
510 // the SchedulerWorker's thread to exit.
TEST(TaskSchedulerWorkerTest,WorkerCleanupFromGetWork)511 TEST(TaskSchedulerWorkerTest, WorkerCleanupFromGetWork) {
512 TaskTracker task_tracker("Test");
513 // Will be owned by SchedulerWorker.
514 MockedControllableCleanupDelegate* delegate =
515 new StrictMock<MockedControllableCleanupDelegate>(&task_tracker);
516 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
517 delegate->controls();
518 controls->set_can_cleanup(true);
519 EXPECT_CALL(*delegate, OnMainEntry(_));
520 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
521 WrapUnique(delegate),
522 task_tracker.GetTrackedRef());
523 worker->Start();
524 worker->WakeUp();
525 controls->WaitForWorkToRun();
526 Mock::VerifyAndClear(delegate);
527 controls->WaitForMainExit();
528 }
529
TEST(TaskSchedulerWorkerTest,WorkerCleanupDuringWork)530 TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWork) {
531 TaskTracker task_tracker("Test");
532 // Will be owned by SchedulerWorker.
533 // No mock here as that's reasonably covered by other tests and the delegate
534 // may destroy on a different thread. Mocks aren't designed with that in mind.
535 std::unique_ptr<ControllableCleanupDelegate> delegate =
536 std::make_unique<ControllableCleanupDelegate>(&task_tracker);
537 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
538 delegate->controls();
539
540 controls->HaveWorkBlock();
541
542 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
543 std::move(delegate),
544 task_tracker.GetTrackedRef());
545 worker->Start();
546 worker->WakeUp();
547
548 controls->WaitForWorkToRun();
549 worker->Cleanup();
550 worker = nullptr;
551 controls->UnblockWork();
552 controls->WaitForDelegateDestroy();
553 }
554
TEST(TaskSchedulerWorkerTest,WorkerCleanupDuringWait)555 TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringWait) {
556 TaskTracker task_tracker("Test");
557 // Will be owned by SchedulerWorker.
558 // No mock here as that's reasonably covered by other tests and the delegate
559 // may destroy on a different thread. Mocks aren't designed with that in mind.
560 std::unique_ptr<ControllableCleanupDelegate> delegate =
561 std::make_unique<ControllableCleanupDelegate>(&task_tracker);
562 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
563 delegate->controls();
564
565 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
566 std::move(delegate),
567 task_tracker.GetTrackedRef());
568 worker->Start();
569 worker->WakeUp();
570
571 controls->WaitForCleanupRequest();
572 worker->Cleanup();
573 worker = nullptr;
574 controls->WaitForDelegateDestroy();
575 }
576
TEST(TaskSchedulerWorkerTest,WorkerCleanupDuringShutdown)577 TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringShutdown) {
578 TaskTracker task_tracker("Test");
579 // Will be owned by SchedulerWorker.
580 // No mock here as that's reasonably covered by other tests and the delegate
581 // may destroy on a different thread. Mocks aren't designed with that in mind.
582 std::unique_ptr<ControllableCleanupDelegate> delegate =
583 std::make_unique<ControllableCleanupDelegate>(&task_tracker);
584 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
585 delegate->controls();
586
587 controls->HaveWorkBlock();
588
589 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
590 std::move(delegate),
591 task_tracker.GetTrackedRef());
592 worker->Start();
593 worker->WakeUp();
594
595 controls->WaitForWorkToRun();
596 task_tracker.Shutdown();
597 worker->Cleanup();
598 worker = nullptr;
599 controls->UnblockWork();
600 controls->WaitForDelegateDestroy();
601 }
602
603 // Verify that Start() is a no-op after Cleanup().
TEST(TaskSchedulerWorkerTest,CleanupBeforeStart)604 TEST(TaskSchedulerWorkerTest, CleanupBeforeStart) {
605 TaskTracker task_tracker("Test");
606 // Will be owned by SchedulerWorker.
607 // No mock here as that's reasonably covered by other tests and the delegate
608 // may destroy on a different thread. Mocks aren't designed with that in mind.
609 std::unique_ptr<ControllableCleanupDelegate> delegate =
610 std::make_unique<ControllableCleanupDelegate>(&task_tracker);
611 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
612 delegate->controls();
613 controls->set_expect_get_work(false);
614
615 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
616 std::move(delegate),
617 task_tracker.GetTrackedRef());
618
619 worker->Cleanup();
620 worker->Start();
621
622 EXPECT_FALSE(worker->ThreadAliveForTesting());
623 }
624
625 namespace {
626
627 class CallJoinFromDifferentThread : public SimpleThread {
628 public:
CallJoinFromDifferentThread(SchedulerWorker * worker_to_join)629 CallJoinFromDifferentThread(SchedulerWorker* worker_to_join)
630 : SimpleThread("SchedulerWorkerJoinThread"),
631 worker_to_join_(worker_to_join) {}
632
633 ~CallJoinFromDifferentThread() override = default;
634
Run()635 void Run() override {
636 run_started_event_.Signal();
637 worker_to_join_->JoinForTesting();
638 }
639
WaitForRunToStart()640 void WaitForRunToStart() { run_started_event_.Wait(); }
641
642 private:
643 SchedulerWorker* const worker_to_join_;
644 WaitableEvent run_started_event_;
645 DISALLOW_COPY_AND_ASSIGN(CallJoinFromDifferentThread);
646 };
647
648 } // namespace
649
TEST(TaskSchedulerWorkerTest,WorkerCleanupDuringJoin)650 TEST(TaskSchedulerWorkerTest, WorkerCleanupDuringJoin) {
651 TaskTracker task_tracker("Test");
652 // Will be owned by SchedulerWorker.
653 // No mock here as that's reasonably covered by other tests and the
654 // delegate may destroy on a different thread. Mocks aren't designed with that
655 // in mind.
656 std::unique_ptr<ControllableCleanupDelegate> delegate =
657 std::make_unique<ControllableCleanupDelegate>(&task_tracker);
658 scoped_refptr<ControllableCleanupDelegate::Controls> controls =
659 delegate->controls();
660
661 controls->HaveWorkBlock();
662
663 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
664 std::move(delegate),
665 task_tracker.GetTrackedRef());
666 worker->Start();
667 worker->WakeUp();
668
669 controls->WaitForWorkToRun();
670 CallJoinFromDifferentThread join_from_different_thread(worker.get());
671 join_from_different_thread.Start();
672 join_from_different_thread.WaitForRunToStart();
673 // Sleep here to give the other thread a chance to call JoinForTesting().
674 // Receiving a signal that Run() was called doesn't mean JoinForTesting() was
675 // necessarily called, and we can't signal after JoinForTesting() as
676 // JoinForTesting() blocks until we call UnblockWork().
677 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
678 worker->Cleanup();
679 worker = nullptr;
680 controls->UnblockWork();
681 controls->WaitForDelegateDestroy();
682 join_from_different_thread.Join();
683 }
684
685 namespace {
686
687 class ExpectThreadPriorityDelegate : public SchedulerWorkerDefaultDelegate {
688 public:
ExpectThreadPriorityDelegate()689 ExpectThreadPriorityDelegate()
690 : priority_verified_in_get_work_event_(
691 WaitableEvent::ResetPolicy::AUTOMATIC,
692 WaitableEvent::InitialState::NOT_SIGNALED),
693 expected_thread_priority_(ThreadPriority::BACKGROUND) {}
694
SetExpectedThreadPriority(ThreadPriority expected_thread_priority)695 void SetExpectedThreadPriority(ThreadPriority expected_thread_priority) {
696 expected_thread_priority_ = expected_thread_priority;
697 }
698
WaitForPriorityVerifiedInGetWork()699 void WaitForPriorityVerifiedInGetWork() {
700 priority_verified_in_get_work_event_.Wait();
701 }
702
703 // SchedulerWorker::Delegate:
OnMainEntry(const SchedulerWorker * worker)704 void OnMainEntry(const SchedulerWorker* worker) override {
705 VerifyThreadPriority();
706 }
GetWork(SchedulerWorker * worker)707 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
708 VerifyThreadPriority();
709 priority_verified_in_get_work_event_.Signal();
710 return nullptr;
711 }
712
713 private:
VerifyThreadPriority()714 void VerifyThreadPriority() {
715 AutoSchedulerLock auto_lock(expected_thread_priority_lock_);
716 EXPECT_EQ(expected_thread_priority_,
717 PlatformThread::GetCurrentThreadPriority());
718 }
719
720 // Signaled after GetWork() has verified the priority of the worker thread.
721 WaitableEvent priority_verified_in_get_work_event_;
722
723 // Synchronizes access to |expected_thread_priority_|.
724 SchedulerLock expected_thread_priority_lock_;
725
726 // Expected thread priority for the next call to OnMainEntry() or GetWork().
727 ThreadPriority expected_thread_priority_;
728
729 DISALLOW_COPY_AND_ASSIGN(ExpectThreadPriorityDelegate);
730 };
731
732 } // namespace
733
TEST(TaskSchedulerWorkerTest,BumpPriorityOfAliveThreadDuringShutdown)734 TEST(TaskSchedulerWorkerTest, BumpPriorityOfAliveThreadDuringShutdown) {
735 if (!CanUseBackgroundPriorityForSchedulerWorker())
736 return;
737
738 TaskTracker task_tracker("Test");
739
740 std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
741 new ExpectThreadPriorityDelegate);
742 ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
743 delegate_raw->SetExpectedThreadPriority(ThreadPriority::BACKGROUND);
744 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::BACKGROUND,
745 std::move(delegate),
746 task_tracker.GetTrackedRef());
747 worker->Start();
748
749 // Verify that the initial thread priority is BACKGROUND (or NORMAL if thread
750 // priority can't be increased).
751 worker->WakeUp();
752 delegate_raw->WaitForPriorityVerifiedInGetWork();
753
754 // Verify that the thread priority is bumped to NORMAL during shutdown.
755 delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
756 task_tracker.SetHasShutdownStartedForTesting();
757 worker->WakeUp();
758 delegate_raw->WaitForPriorityVerifiedInGetWork();
759
760 worker->JoinForTesting();
761 }
762
763 namespace {
764
765 class VerifyCallsToObserverDelegate : public SchedulerWorkerDefaultDelegate {
766 public:
VerifyCallsToObserverDelegate(test::MockSchedulerWorkerObserver * observer)767 VerifyCallsToObserverDelegate(test::MockSchedulerWorkerObserver* observer)
768 : observer_(observer) {}
769
770 // SchedulerWorker::Delegate:
OnMainEntry(const SchedulerWorker * worker)771 void OnMainEntry(const SchedulerWorker* worker) override {
772 Mock::VerifyAndClear(observer_);
773 }
774
OnMainExit(SchedulerWorker * worker)775 void OnMainExit(SchedulerWorker* worker) override {
776 EXPECT_CALL(*observer_, OnSchedulerWorkerMainExit());
777 }
778
779 private:
780 test::MockSchedulerWorkerObserver* const observer_;
781
782 DISALLOW_COPY_AND_ASSIGN(VerifyCallsToObserverDelegate);
783 };
784
785 } // namespace
786
787 // Flaky: crbug.com/846121
788 #if defined(OS_LINUX) && defined(ADDRESS_SANITIZER)
789 #define MAYBE_SchedulerWorkerObserver DISABLED_SchedulerWorkerObserver
790 #else
791 #define MAYBE_SchedulerWorkerObserver SchedulerWorkerObserver
792 #endif
793
794 // Verify that the SchedulerWorkerObserver is notified when the worker enters
795 // and exits its main function.
TEST(TaskSchedulerWorkerTest,MAYBE_SchedulerWorkerObserver)796 TEST(TaskSchedulerWorkerTest, MAYBE_SchedulerWorkerObserver) {
797 StrictMock<test::MockSchedulerWorkerObserver> observer;
798 {
799 TaskTracker task_tracker("Test");
800 auto delegate = std::make_unique<VerifyCallsToObserverDelegate>(&observer);
801 auto worker = MakeRefCounted<SchedulerWorker>(ThreadPriority::NORMAL,
802 std::move(delegate),
803 task_tracker.GetTrackedRef());
804
805 EXPECT_CALL(observer, OnSchedulerWorkerMainEntry());
806 worker->Start(&observer);
807 worker->Cleanup();
808 worker = nullptr;
809 }
810 Mock::VerifyAndClear(&observer);
811 }
812
813 #if defined(OS_WIN)
814
815 namespace {
816
817 class CoInitializeDelegate : public SchedulerWorkerDefaultDelegate {
818 public:
819 CoInitializeDelegate() = default;
820
GetWork(SchedulerWorker * worker)821 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
822 EXPECT_FALSE(get_work_returned_.IsSignaled());
823 EXPECT_EQ(E_UNEXPECTED, coinitialize_hresult_);
824
825 coinitialize_hresult_ = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
826 if (SUCCEEDED(coinitialize_hresult_))
827 CoUninitialize();
828
829 get_work_returned_.Signal();
830 return nullptr;
831 }
832
WaitUntilGetWorkReturned()833 void WaitUntilGetWorkReturned() { get_work_returned_.Wait(); }
834
coinitialize_hresult() const835 HRESULT coinitialize_hresult() const { return coinitialize_hresult_; }
836
837 private:
838 WaitableEvent get_work_returned_;
839 HRESULT coinitialize_hresult_ = E_UNEXPECTED;
840
841 DISALLOW_COPY_AND_ASSIGN(CoInitializeDelegate);
842 };
843
844 } // namespace
845
TEST(TaskSchedulerWorkerTest,BackwardCompatibilityEnabled)846 TEST(TaskSchedulerWorkerTest, BackwardCompatibilityEnabled) {
847 TaskTracker task_tracker("Test");
848 auto delegate = std::make_unique<CoInitializeDelegate>();
849 CoInitializeDelegate* const delegate_raw = delegate.get();
850
851 // Create a worker with backward compatibility ENABLED. Wake it up and wait
852 // until GetWork() returns.
853 auto worker = MakeRefCounted<SchedulerWorker>(
854 ThreadPriority::NORMAL, std::move(delegate), task_tracker.GetTrackedRef(),
855 nullptr, SchedulerBackwardCompatibility::INIT_COM_STA);
856 worker->Start();
857 worker->WakeUp();
858 delegate_raw->WaitUntilGetWorkReturned();
859
860 // The call to CoInitializeEx() should have returned S_FALSE to indicate that
861 // the COM library was already initialized on the thread.
862 // See SchedulerWorker::Thread::ThreadMain for why we expect two different
863 // results here.
864 #if defined(COM_INIT_CHECK_HOOK_ENABLED)
865 EXPECT_EQ(S_OK, delegate_raw->coinitialize_hresult());
866 #else
867 EXPECT_EQ(S_FALSE, delegate_raw->coinitialize_hresult());
868 #endif
869
870 worker->JoinForTesting();
871 }
872
TEST(TaskSchedulerWorkerTest,BackwardCompatibilityDisabled)873 TEST(TaskSchedulerWorkerTest, BackwardCompatibilityDisabled) {
874 TaskTracker task_tracker("Test");
875 auto delegate = std::make_unique<CoInitializeDelegate>();
876 CoInitializeDelegate* const delegate_raw = delegate.get();
877
878 // Create a worker with backward compatibility DISABLED. Wake it up and wait
879 // until GetWork() returns.
880 auto worker = MakeRefCounted<SchedulerWorker>(
881 ThreadPriority::NORMAL, std::move(delegate), task_tracker.GetTrackedRef(),
882 nullptr, SchedulerBackwardCompatibility::DISABLED);
883 worker->Start();
884 worker->WakeUp();
885 delegate_raw->WaitUntilGetWorkReturned();
886
887 // The call to CoInitializeEx() should have returned S_OK to indicate that the
888 // COM library wasn't already initialized on the thread.
889 EXPECT_EQ(S_OK, delegate_raw->coinitialize_hresult());
890
891 worker->JoinForTesting();
892 }
893
894 #endif // defined(OS_WIN)
895
896 } // namespace internal
897 } // namespace base
898