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

Side by Side Diff: base/task_scheduler/scheduler_worker_unittest.cc~RF3e0a7fad.TMP

Issue 2276423002: Remove temporary file that was accidentally checked in (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task_scheduler/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/synchronization/condition_variable.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/task_scheduler/scheduler_lock.h"
19 #include "base/task_scheduler/sequence.h"
20 #include "base/task_scheduler/task.h"
21 #include "base/task_scheduler/task_tracker.h"
22 #include "base/threading/platform_thread.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using testing::_;
29 using testing::Mock;
30 using testing::Ne;
31 using testing::StrictMock;
32
33 namespace base {
34 namespace internal {
35 namespace {
36
37 const size_t kNumSequencesPerTest = 150;
38
39 class SchedulerWorkerDefaultDelegate : public SchedulerWorker::Delegate {
40 public:
41 SchedulerWorkerDefaultDelegate() = default;
42
43 // SchedulerWorker::Delegate:
44 void OnMainEntry(SchedulerWorker* worker, const TimeDelta& detach_duration) ov erride {}
45 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { return nul lptr; }
46 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
47 ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
48 }
49 TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
50 bool CanDetach(SchedulerWorker* worker) override { return false; }
51
52 private:
53 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDefaultDelegate);
54 };
55
56 // The test parameter is the number of Tasks per Sequence returned by GetWork().
57 class TaskSchedulerWorkerTest : public testing::TestWithParam<size_t> {
58 protected:
59 TaskSchedulerWorkerTest()
60 : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL,
61 WaitableEvent::InitialState::NOT_SIGNALED),
62 num_get_work_cv_(lock_.CreateConditionVariable()),
63 worker_set_(WaitableEvent::ResetPolicy::MANUAL,
64 WaitableEvent::InitialState::NOT_SIGNALED) {}
65
66 void SetUp() override {
67 worker_ = SchedulerWorker::Create(
68 ThreadPriority::NORMAL,
69 WrapUnique(new TestSchedulerWorkerDelegate(this)),
70 &task_tracker_,
71 SchedulerWorker::InitialState::ALIVE);
72 ASSERT_TRUE(worker_);
73 worker_set_.Signal();
74 main_entry_called_.Wait();
75 }
76
77 void TearDown() override {
78 worker_->JoinForTesting();
79 }
80
81 size_t TasksPerSequence() const { return GetParam(); }
82
83 // Wait until GetWork() has been called |num_get_work| times.
84 void WaitForNumGetWork(size_t num_get_work) {
85 AutoSchedulerLock auto_lock(lock_);
86 while (num_get_work_ < num_get_work)
87 num_get_work_cv_->Wait();
88 }
89
90 void SetMaxGetWork(size_t max_get_work) {
91 AutoSchedulerLock auto_lock(lock_);
92 max_get_work_ = max_get_work;
93 }
94
95 void SetNumSequencesToCreate(size_t num_sequences_to_create) {
96 AutoSchedulerLock auto_lock(lock_);
97 EXPECT_EQ(0U, num_sequences_to_create_);
98 num_sequences_to_create_ = num_sequences_to_create;
99 }
100
101 size_t NumRunTasks() {
102 AutoSchedulerLock auto_lock(lock_);
103 return num_run_tasks_;
104 }
105
106 std::vector<scoped_refptr<Sequence>> CreatedSequences() {
107 AutoSchedulerLock auto_lock(lock_);
108 return created_sequences_;
109 }
110
111 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() {
112 AutoSchedulerLock auto_lock(lock_);
113 return re_enqueued_sequences_;
114 }
115
116 std::unique_ptr<SchedulerWorker> worker_;
117
118 private:
119 class TestSchedulerWorkerDelegate : public SchedulerWorkerDefaultDelegate {
120 public:
121 TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest* outer)
122 : outer_(outer) {}
123
124 // SchedulerWorker::Delegate:
125 void OnMainEntry(SchedulerWorker* worker,
126 const TimeDelta& detach_duration) override {
127 outer_->worker_set_.Wait();
128 EXPECT_EQ(outer_->worker_.get(), worker);
129
130 // Without synchronization, OnMainEntry() could be called twice without
131 // generating an error.
132 AutoSchedulerLock auto_lock(outer_->lock_);
133 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled());
134 outer_->main_entry_called_.Signal();
135 }
136
137 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
138 EXPECT_EQ(outer_->worker_.get(), worker);
139
140 {
141 AutoSchedulerLock auto_lock(outer_->lock_);
142
143 // Increment the number of times that this method has been called.
144 ++outer_->num_get_work_;
145 outer_->num_get_work_cv_->Signal();
146
147 // Verify that this method isn't called more times than expected.
148 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_);
149
150 // Check if a Sequence should be returned.
151 if (outer_->num_sequences_to_create_ == 0)
152 return nullptr;
153 --outer_->num_sequences_to_create_;
154 }
155
156 // Create a Sequence with TasksPerSequence() Tasks.
157 scoped_refptr<Sequence> sequence(new Sequence);
158 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) {
159 std::unique_ptr<Task> task(new Task(
160 FROM_HERE, Bind(&TaskSchedulerWorkerTest::RunTaskCallback,
161 Unretained(outer_)),
162 TaskTraits(), TimeDelta()));
163 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get()));
164 sequence->PushTask(std::move(task));
165 }
166
167 {
168 // Add the Sequence to the vector of created Sequences.
169 AutoSchedulerLock auto_lock(outer_->lock_);
170 outer_->created_sequences_.push_back(sequence);
171 }
172
173 return sequence;
174 }
175
176 // This override verifies that |sequence| contains the expected number of
177 // Tasks and adds it to |enqueued_sequences_|. Unlike a normal
178 // EnqueueSequence implementation, it doesn't reinsert |sequence| into a
179 // queue for further execution.
180 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
181 EXPECT_GT(outer_->TasksPerSequence(), 1U);
182
183 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks.
184 for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) {
185 EXPECT_TRUE(sequence->PeekTask());
186 sequence->PopTask();
187 }
188 EXPECT_FALSE(sequence->PeekTask());
189
190 // Add |sequence| to |re_enqueued_sequences_|.
191 AutoSchedulerLock auto_lock(outer_->lock_);
192 outer_->re_enqueued_sequences_.push_back(std::move(sequence));
193 EXPECT_LE(outer_->re_enqueued_sequences_.size(),
194 outer_->created_sequences_.size());
195 }
196
197 private:
198 TaskSchedulerWorkerTest* outer_;
199 };
200
201 void RunTaskCallback() {
202 AutoSchedulerLock auto_lock(lock_);
203 ++num_run_tasks_;
204 EXPECT_LE(num_run_tasks_, created_sequences_.size());
205 }
206
207 TaskTracker task_tracker_;
208
209 // Synchronizes access to all members below.
210 mutable SchedulerLock lock_;
211
212 // Signaled once OnMainEntry() has been called.
213 WaitableEvent main_entry_called_;
214
215 // Number of Sequences that should be created by GetWork(). When this
216 // is 0, GetWork() returns nullptr.
217 size_t num_sequences_to_create_ = 0;
218
219 // Number of times that GetWork() has been called.
220 size_t num_get_work_ = 0;
221
222 // Maximum number of times that GetWork() can be called.
223 size_t max_get_work_ = 0;
224
225 // Condition variable signaled when |num_get_work_| is incremented.
226 std::unique_ptr<ConditionVariable> num_get_work_cv_;
227
228 // Sequences created by GetWork().
229 std::vector<scoped_refptr<Sequence>> created_sequences_;
230
231 // Sequences passed to EnqueueSequence().
232 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_;
233
234 // Number of times that RunTaskCallback() has been called.
235 size_t num_run_tasks_ = 0;
236
237 // Signaled after |worker_| is set.
238 WaitableEvent worker_set_;
239
240 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerTest);
241 };
242
243 // Verify that when GetWork() continuously returns Sequences, all Tasks in these
244 // Sequences run successfully. The test wakes up the SchedulerWorker once.
245 TEST_P(TaskSchedulerWorkerTest, ContinuousWork) {
246 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to
247 // return nullptr.
248 SetNumSequencesToCreate(kNumSequencesPerTest);
249
250 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a
251 // Sequence and one call in which its returns nullptr.
252 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1;
253 SetMaxGetWork(kExpectedNumGetWork);
254
255 // Wake up |worker_| and wait until GetWork() has been invoked the
256 // expected amount of times.
257 worker_->WakeUp();
258 WaitForNumGetWork(kExpectedNumGetWork);
259
260 // All tasks should have run.
261 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks());
262
263 // If Sequences returned by GetWork() contain more than one Task, they aren't
264 // empty after the worker pops Tasks from them and thus should be returned to
265 // EnqueueSequence().
266 if (TasksPerSequence() > 1)
267 EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
268 else
269 EXPECT_TRUE(EnqueuedSequences().empty());
270 }
271
272 // Verify that when GetWork() alternates between returning a Sequence and
273 // returning nullptr, all Tasks in the returned Sequences run successfully. The
274 // test wakes up the SchedulerWorker once for each Sequence.
275 TEST_P(TaskSchedulerWorkerTest, IntermittentWork) {
276 for (size_t i = 0; i < kNumSequencesPerTest; ++i) {
277 // Set GetWork() to return 1 Sequence before starting to return
278 // nullptr.
279 SetNumSequencesToCreate(1);
280
281 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and
282 // |i + 1| calls in which it returns nullptr.
283 const size_t expected_num_get_work = 2 * (i + 1);
284 SetMaxGetWork(expected_num_get_work);
285
286 // Wake up |worker_| and wait until GetWork() has been invoked
287 // the expected amount of times.
288 worker_->WakeUp();
289 WaitForNumGetWork(expected_num_get_work);
290
291 // The Task should have run
292 EXPECT_EQ(i + 1, NumRunTasks());
293
294 // If Sequences returned by GetWork() contain more than one Task, they
295 // aren't empty after the worker pops Tasks from them and thus should be
296 // returned to EnqueueSequence().
297 if (TasksPerSequence() > 1)
298 EXPECT_EQ(CreatedSequences(), EnqueuedSequences());
299 else
300 EXPECT_TRUE(EnqueuedSequences().empty());
301 }
302 }
303
304 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence,
305 TaskSchedulerWorkerTest,
306 ::testing::Values(1));
307 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence,
308 TaskSchedulerWorkerTest,
309 ::testing::Values(2));
310
311 namespace {
312
313 class ControllableDetachDelegate : public SchedulerWorkerDefaultDelegate {
314 public:
315 ControllableDetachDelegate()
316 : work_processed_(WaitableEvent::ResetPolicy::MANUAL,
317 WaitableEvent::InitialState::NOT_SIGNALED),
318 detach_requested_(WaitableEvent::ResetPolicy::MANUAL,
319 WaitableEvent::InitialState::NOT_SIGNALED) {}
320
321 ~ControllableDetachDelegate() override = default;
322
323 // SchedulerWorker::Delegate:
324 MOCK_METHOD2(OnMainEntry,
325 void(SchedulerWorker* worker, const TimeDelta& detach_duration));
326
327 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker)
328 override {
329 // Sends one item of work to signal |work_processed_|. On subsequent calls,
330 // sends nullptr to indicate there's no more work to be done.
331 if (work_requested_)
332 return nullptr;
333
334 work_requested_ = true;
335 scoped_refptr<Sequence> sequence(new Sequence);
336 std::unique_ptr<Task> task(new Task(
337 FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&work_processed_)),
338 TaskTraits(), TimeDelta()));
339 sequence->PushTask(std::move(task));
340 return sequence;
341 }
342
343 bool CanDetach(SchedulerWorker* worker) override {
344 detach_requested_.Signal();
345 return can_detach_;
346 }
347
348 void WaitForWorkToRun() {
349 work_processed_.Wait();
350 }
351
352 void WaitForDetachRequest() {
353 detach_requested_.Wait();
354 }
355
356 void ResetState() {
357 work_requested_ = false;
358 work_processed_.Reset();
359 detach_requested_.Reset();
360 }
361
362 void set_can_detach(bool can_detach) { can_detach_ = can_detach; }
363
364 private:
365 bool work_requested_ = false;
366 bool can_detach_ = false;
367 WaitableEvent work_processed_;
368 WaitableEvent detach_requested_;
369
370 DISALLOW_COPY_AND_ASSIGN(ControllableDetachDelegate);
371 };
372
373 } // namespace
374
375 TEST(TaskSchedulerWorkerTest, WorkerDetaches) {
376 TaskTracker task_tracker;
377 // Will be owned by SchedulerWorker.
378 ControllableDetachDelegate* delegate =
379 new StrictMock<ControllableDetachDelegate>;
380 delegate->set_can_detach(true);
381 EXPECT_CALL(*delegate, OnMainEntry(_, TimeDelta::Max()));
382 std::unique_ptr<SchedulerWorker> worker =
383 SchedulerWorker::Create(
384 ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
385 SchedulerWorker::InitialState::ALIVE);
386 worker->WakeUp();
387 delegate->WaitForWorkToRun();
388 Mock::VerifyAndClear(delegate);
389 delegate->WaitForDetachRequest();
390 // Sleep to give a chance for the detach to happen. A yield is too short.
391 PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
392 ASSERT_FALSE(worker->ThreadAliveForTesting());
393 }
394
395 TEST(TaskSchedulerWorkerTest, WorkerDetachesAndWakes) {
396 TaskTracker task_tracker;
397 // Will be owned by SchedulerWorker.
398 ControllableDetachDelegate* delegate =
399 new StrictMock<ControllableDetachDelegate>;
400 delegate->set_can_detach(true);
401 EXPECT_CALL(*delegate, OnMainEntry(_, TimeDelta::Max()));
402 std::unique_ptr<SchedulerWorker> worker =
403 SchedulerWorker::Create(
404 ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
405 SchedulerWorker::InitialState::ALIVE);
406 worker->WakeUp();
407 delegate->WaitForWorkToRun();
408 Mock::VerifyAndClear(delegate);
409 delegate->WaitForDetachRequest();
410 // Sleep to give a chance for the detach to happen. A yield is too short.
411 PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
412 ASSERT_FALSE(worker->ThreadAliveForTesting());
413
414 delegate->ResetState();
415 delegate->set_can_detach(false);
416 // When SchedulerWorker recreates its thread, expect OnMainEntry() to be
417 // called with a detach duration which is not TimeDelta::Max().
418 EXPECT_CALL(*delegate, OnMainEntry(worker.get(), Ne(TimeDelta::Max())));
419 worker->WakeUp();
420 delegate->WaitForWorkToRun();
421 Mock::VerifyAndClear(delegate);
422 delegate->WaitForDetachRequest();
423 PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
424 ASSERT_TRUE(worker->ThreadAliveForTesting());
425 worker->JoinForTesting();
426 }
427
428 TEST(TaskSchedulerWorkerTest, CreateDetached) {
429 TaskTracker task_tracker;
430 // Will be owned by SchedulerWorker.
431 ControllableDetachDelegate* delegate =
432 new StrictMock<ControllableDetachDelegate>;
433 std::unique_ptr<SchedulerWorker> worker =
434 SchedulerWorker::Create(
435 ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker,
436 SchedulerWorker::InitialState::DETACHED);
437 ASSERT_FALSE(worker->ThreadAliveForTesting());
438 EXPECT_CALL(*delegate, OnMainEntry(worker.get(), TimeDelta::Max()));
439 worker->WakeUp();
440 delegate->WaitForWorkToRun();
441 Mock::VerifyAndClear(delegate);
442 delegate->WaitForDetachRequest();
443 ASSERT_TRUE(worker->ThreadAliveForTesting());
444 worker->JoinForTesting();
445 }
446
447 namespace {
448
449 class ExpectThreadPriorityDelegate : public SchedulerWorkerDefaultDelegate {
450 public:
451 ExpectThreadPriorityDelegate()
452 : priority_verified_in_get_work_event_(
453 WaitableEvent::ResetPolicy::AUTOMATIC,
454 WaitableEvent::InitialState::NOT_SIGNALED),
455 expected_thread_priority_(ThreadPriority::BACKGROUND) {}
456
457 void SetExpectedThreadPriority(ThreadPriority expected_thread_priority) {
458 expected_thread_priority_ = expected_thread_priority;
459 }
460
461 void WaitForPriorityVerifiedInGetWork() {
462 priority_verified_in_get_work_event_.Wait();
463 }
464
465 // SchedulerWorker::Delegate:
466 void OnMainEntry(SchedulerWorker* worker,
467 const TimeDelta& detach_duration) override {
468 VerifyThreadPriority();
469 }
470 scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
471 VerifyThreadPriority();
472 priority_verified_in_get_work_event_.Signal();
473 return nullptr;
474 }
475
476 private:
477 void VerifyThreadPriority() {
478 AutoSchedulerLock auto_lock(expected_thread_priority_lock_);
479 EXPECT_EQ(expected_thread_priority_,
480 PlatformThread::GetCurrentThreadPriority());
481 }
482
483 // Signaled after GetWork() has verified the priority of the worker thread.
484 WaitableEvent priority_verified_in_get_work_event_;
485
486 // Synchronizes access to |expected_thread_priority_|.
487 SchedulerLock expected_thread_priority_lock_;
488
489 // Expected thread priority for the next call to OnMainEntry() or GetWork().
490 ThreadPriority expected_thread_priority_;
491
492 DISALLOW_COPY_AND_ASSIGN(ExpectThreadPriorityDelegate);
493 };
494
495 } // namespace
496
497 TEST(TaskSchedulerWorkerTest, BumpPriorityOfAliveThreadDuringShutdown) {
498 TaskTracker task_tracker;
499
500 std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
501 new ExpectThreadPriorityDelegate);
502 ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
503 delegate_raw->SetExpectedThreadPriority(
504 PlatformThread::CanIncreaseCurrentThreadPriority()
505 ? ThreadPriority::BACKGROUND
506 : ThreadPriority::NORMAL);
507
508 std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
509 ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker,
510 SchedulerWorker::InitialState::ALIVE);
511
512 // Verify that the initial thread priority is BACKGROUND (or NORMAL if thread
513 // priority can't be increased).
514 worker->WakeUp();
515 delegate_raw->WaitForPriorityVerifiedInGetWork();
516
517 // Verify that the thread priority is bumped to NORMAL during shutdown.
518 delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
519 task_tracker.SetHasShutdownStartedForTesting();
520 worker->WakeUp();
521 delegate_raw->WaitForPriorityVerifiedInGetWork();
522
523 worker->JoinForTesting();
524 }
525
526 TEST(TaskSchedulerWorkerTest, BumpPriorityOfDetachedThreadDuringShutdown) {
527 TaskTracker task_tracker;
528
529 std::unique_ptr<ExpectThreadPriorityDelegate> delegate(
530 new ExpectThreadPriorityDelegate);
531 ExpectThreadPriorityDelegate* delegate_raw = delegate.get();
532 delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL);
533
534 // Create a DETACHED thread.
535 std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create(
536 ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker,
537 SchedulerWorker::InitialState::DETACHED);
538
539 // Pretend that shutdown has started.
540 task_tracker.SetHasShutdownStartedForTesting();
541
542 // Wake up the thread and verify that its priority is NORMAL when
543 // OnMainEntry() and GetWork() are called.
544 worker->WakeUp();
545 delegate_raw->WaitForPriorityVerifiedInGetWork();
546
547 worker->JoinForTesting();
548 }
549
550 } // namespace
551 } // namespace internal
552 } // namespace base
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698