1 // Copyright 2019 The Chromium Authors
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/thread_pool/job_task_source.h"
6
7 #include <utility>
8
9 #include "base/functional/callback_helpers.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/task/thread_pool/pooled_task_runner_delegate.h"
12 #include "base/task/thread_pool/test_utils.h"
13 #include "base/test/bind.h"
14 #include "base/test/gtest_util.h"
15 #include "base/test/test_timeouts.h"
16 #include "build/build_config.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using ::testing::_;
21 using ::testing::Return;
22
23 namespace base {
24 namespace internal {
25
26 class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate {
27 public:
28 MOCK_METHOD2(PostTaskWithSequence,
29 bool(Task task, scoped_refptr<Sequence> sequence));
30 MOCK_METHOD1(ShouldYield, bool(const TaskSource* task_source));
31 MOCK_METHOD1(EnqueueJobTaskSource,
32 bool(scoped_refptr<JobTaskSource> task_source));
33 MOCK_METHOD1(RemoveJobTaskSource,
34 void(scoped_refptr<JobTaskSource> task_source));
35 MOCK_CONST_METHOD1(IsRunningPoolWithTraits, bool(const TaskTraits& traits));
36 MOCK_METHOD2(UpdatePriority,
37 void(scoped_refptr<TaskSource> task_source,
38 TaskPriority priority));
39 MOCK_METHOD2(UpdateJobPriority,
40 void(scoped_refptr<TaskSource> task_source,
41 TaskPriority priority));
42 };
43
44 class ThreadPoolJobTaskSourceTest : public testing::Test {
45 protected:
46 testing::StrictMock<MockPooledTaskRunnerDelegate>
47 pooled_task_runner_delegate_;
48 };
49
50 // Verifies the normal flow of running 2 tasks one after the other.
TEST_F(ThreadPoolJobTaskSourceTest,RunTasks)51 TEST_F(ThreadPoolJobTaskSourceTest, RunTasks) {
52 auto job_task = base::MakeRefCounted<test::MockJobTask>(
53 DoNothing(), /* num_tasks_to_run */ 2);
54 scoped_refptr<JobTaskSource> task_source =
55 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
56 auto registered_task_source =
57 RegisteredTaskSource::CreateForTesting(task_source);
58
59 EXPECT_EQ(2U, task_source->GetRemainingConcurrency());
60 {
61 EXPECT_EQ(registered_task_source.WillRunTask(),
62 TaskSource::RunStatus::kAllowedNotSaturated);
63 EXPECT_EQ(1U, task_source->GetWorkerCount());
64
65 auto task = registered_task_source.TakeTask();
66 std::move(task.task).Run();
67 EXPECT_TRUE(registered_task_source.DidProcessTask());
68 EXPECT_EQ(0U, task_source->GetWorkerCount());
69 }
70 {
71 EXPECT_EQ(registered_task_source.WillRunTask(),
72 TaskSource::RunStatus::kAllowedSaturated);
73 EXPECT_EQ(1U, task_source->GetWorkerCount());
74
75 // An attempt to run an additional task is not allowed.
76 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
77 TaskSource::RunStatus::kDisallowed);
78 EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
79 auto task = registered_task_source.TakeTask();
80 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
81 TaskSource::RunStatus::kDisallowed);
82
83 std::move(task.task).Run();
84 EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
85 EXPECT_TRUE(task_source->IsActive());
86 // Returns false because the task source is out of tasks.
87 EXPECT_FALSE(registered_task_source.DidProcessTask());
88 EXPECT_EQ(0U, task_source->GetWorkerCount());
89 EXPECT_FALSE(task_source->IsActive());
90 }
91 }
92
93 // Verifies that a job task source doesn't allow any new RunStatus after Clear()
94 // is called.
TEST_F(ThreadPoolJobTaskSourceTest,Clear)95 TEST_F(ThreadPoolJobTaskSourceTest, Clear) {
96 auto job_task = base::MakeRefCounted<test::MockJobTask>(
97 DoNothing(), /* num_tasks_to_run */ 5);
98 scoped_refptr<JobTaskSource> task_source =
99 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
100
101 EXPECT_EQ(5U, task_source->GetRemainingConcurrency());
102 auto registered_task_source_a =
103 RegisteredTaskSource::CreateForTesting(task_source);
104 EXPECT_EQ(registered_task_source_a.WillRunTask(),
105 TaskSource::RunStatus::kAllowedNotSaturated);
106 auto task_a = registered_task_source_a.TakeTask();
107
108 auto registered_task_source_b =
109 RegisteredTaskSource::CreateForTesting(task_source);
110 EXPECT_EQ(registered_task_source_b.WillRunTask(),
111 TaskSource::RunStatus::kAllowedNotSaturated);
112
113 auto registered_task_source_c =
114 RegisteredTaskSource::CreateForTesting(task_source);
115 EXPECT_EQ(registered_task_source_c.WillRunTask(),
116 TaskSource::RunStatus::kAllowedNotSaturated);
117
118 auto registered_task_source_d =
119 RegisteredTaskSource::CreateForTesting(task_source);
120 EXPECT_EQ(registered_task_source_d.WillRunTask(),
121 TaskSource::RunStatus::kAllowedNotSaturated);
122
123 EXPECT_FALSE(task_source->ShouldYield());
124
125 {
126 EXPECT_EQ(1U, task_source->GetRemainingConcurrency());
127 auto task = registered_task_source_c.Clear();
128 EXPECT_FALSE(task);
129 registered_task_source_c.DidProcessTask();
130 EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
131 }
132 // The task source shouldn't allow any further tasks after Clear.
133 EXPECT_TRUE(task_source->ShouldYield());
134 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
135 TaskSource::RunStatus::kDisallowed);
136
137 // Another outstanding RunStatus can still call Clear.
138 {
139 auto task = registered_task_source_d.Clear();
140 EXPECT_FALSE(task);
141 registered_task_source_d.DidProcessTask();
142 EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
143 }
144
145 // A task that was already acquired can still run.
146 std::move(task_a.task).Run();
147 registered_task_source_a.DidProcessTask();
148
149 // A valid outstanding RunStatus can also take and run a task.
150 {
151 auto task = registered_task_source_b.TakeTask();
152 std::move(task.task).Run();
153 registered_task_source_b.DidProcessTask();
154 }
155 }
156
157 // Verifies that a job task source doesn't return an "allowed" RunStatus after
158 // Cancel() is called.
TEST_F(ThreadPoolJobTaskSourceTest,Cancel)159 TEST_F(ThreadPoolJobTaskSourceTest, Cancel) {
160 auto job_task = base::MakeRefCounted<test::MockJobTask>(
161 DoNothing(), /* num_tasks_to_run */ 3);
162 scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
163 FROM_HERE, {TaskPriority::BEST_EFFORT}, &pooled_task_runner_delegate_);
164
165 auto registered_task_source_a =
166 RegisteredTaskSource::CreateForTesting(task_source);
167 EXPECT_EQ(registered_task_source_a.WillRunTask(),
168 TaskSource::RunStatus::kAllowedNotSaturated);
169 auto task_a = registered_task_source_a.TakeTask();
170
171 auto registered_task_source_b =
172 RegisteredTaskSource::CreateForTesting(task_source);
173 EXPECT_EQ(registered_task_source_b.WillRunTask(),
174 TaskSource::RunStatus::kAllowedNotSaturated);
175
176 EXPECT_FALSE(task_source->ShouldYield());
177
178 task_source->Cancel();
179 EXPECT_TRUE(task_source->ShouldYield());
180
181 // The task source shouldn't allow any further tasks after Cancel.
182 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
183 TaskSource::RunStatus::kDisallowed);
184
185 // A task that was already acquired can still run.
186 std::move(task_a.task).Run();
187 registered_task_source_a.DidProcessTask();
188
189 // A RegisteredTaskSource that's ready can also take and run a task.
190 {
191 auto task = registered_task_source_b.TakeTask();
192 std::move(task.task).Run();
193 registered_task_source_b.DidProcessTask();
194 }
195 }
196
197 // Verifies that multiple tasks can run in parallel up to |max_concurrency|.
TEST_F(ThreadPoolJobTaskSourceTest,RunTasksInParallel)198 TEST_F(ThreadPoolJobTaskSourceTest, RunTasksInParallel) {
199 auto job_task = base::MakeRefCounted<test::MockJobTask>(
200 DoNothing(), /* num_tasks_to_run */ 2);
201 scoped_refptr<JobTaskSource> task_source =
202 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
203
204 auto registered_task_source_a =
205 RegisteredTaskSource::CreateForTesting(task_source);
206 EXPECT_EQ(registered_task_source_a.WillRunTask(),
207 TaskSource::RunStatus::kAllowedNotSaturated);
208 EXPECT_EQ(1U, task_source->GetWorkerCount());
209 EXPECT_EQ(1U, task_source->GetSortKey().worker_count());
210 auto task_a = registered_task_source_a.TakeTask();
211
212 auto registered_task_source_b =
213 RegisteredTaskSource::CreateForTesting(task_source);
214 EXPECT_EQ(registered_task_source_b.WillRunTask(),
215 TaskSource::RunStatus::kAllowedSaturated);
216 EXPECT_EQ(2U, task_source->GetWorkerCount());
217 EXPECT_EQ(2U, task_source->GetSortKey().worker_count());
218 auto task_b = registered_task_source_b.TakeTask();
219
220 // WillRunTask() should return a null RunStatus once the max concurrency is
221 // reached.
222 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
223 TaskSource::RunStatus::kDisallowed);
224
225 std::move(task_a.task).Run();
226 // Adding a task before closing the first run operation should cause the task
227 // source to re-enqueue.
228 job_task->SetNumTasksToRun(2);
229 EXPECT_TRUE(registered_task_source_a.DidProcessTask());
230 EXPECT_EQ(1U, task_source->GetSortKey().worker_count());
231
232 std::move(task_b.task).Run();
233 EXPECT_TRUE(registered_task_source_b.DidProcessTask());
234 EXPECT_EQ(0U, task_source->GetSortKey().worker_count());
235
236 EXPECT_EQ(0U, task_source->GetWorkerCount());
237
238 auto registered_task_source_c =
239 RegisteredTaskSource::CreateForTesting(task_source);
240 EXPECT_EQ(registered_task_source_c.WillRunTask(),
241 TaskSource::RunStatus::kAllowedSaturated);
242 auto task_c = registered_task_source_c.TakeTask();
243
244 std::move(task_c.task).Run();
245 EXPECT_FALSE(registered_task_source_c.DidProcessTask());
246 }
247
248 // Verifies the normal flow of running the join task until completion.
TEST_F(ThreadPoolJobTaskSourceTest,RunJoinTask)249 TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTask) {
250 auto job_task = base::MakeRefCounted<test::MockJobTask>(
251 DoNothing(), /* num_tasks_to_run */ 2);
252 scoped_refptr<JobTaskSource> task_source =
253 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
254
255 EXPECT_TRUE(task_source->WillJoin());
256 // Intentionally run |worker_task| twice to make sure RunJoinTask() calls
257 // it again. This can happen in production if the joining thread spuriously
258 // return and needs to run again.
259 EXPECT_TRUE(task_source->RunJoinTask());
260 EXPECT_FALSE(task_source->RunJoinTask());
261 }
262
263 // Verify that |worker_count| excludes the (inactive) returning thread calling
264 // max_concurrency_callback.
TEST_F(ThreadPoolJobTaskSourceTest,RunTaskWorkerCount)265 TEST_F(ThreadPoolJobTaskSourceTest, RunTaskWorkerCount) {
266 size_t max_concurrency = 1;
267 scoped_refptr<JobTaskSource> task_source =
268 base::MakeRefCounted<JobTaskSource>(
269 FROM_HERE, TaskTraits(),
270 BindLambdaForTesting(
271 [&](JobDelegate* delegate) { --max_concurrency; }),
272 BindLambdaForTesting([&](size_t worker_count) -> size_t {
273 return max_concurrency + worker_count;
274 }),
275 &pooled_task_runner_delegate_);
276
277 auto registered_task_source =
278 RegisteredTaskSource::CreateForTesting(task_source);
279
280 EXPECT_EQ(registered_task_source.WillRunTask(),
281 TaskSource::RunStatus::kAllowedSaturated);
282 auto task = registered_task_source.TakeTask();
283 std::move(task.task).Run();
284 // Once the worker_task runs, |worker_count| should drop to 0 and the job
285 // should finish.
286 EXPECT_FALSE(registered_task_source.DidProcessTask());
287 EXPECT_EQ(0U, max_concurrency);
288 }
289
290 // Verify that |worker_count| excludes the (inactive) joining thread calling
291 // max_concurrency_callback.
TEST_F(ThreadPoolJobTaskSourceTest,RunJoinTaskWorkerCount)292 TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTaskWorkerCount) {
293 size_t max_concurrency = 1;
294 scoped_refptr<JobTaskSource> task_source =
295 base::MakeRefCounted<JobTaskSource>(
296 FROM_HERE, TaskTraits(),
297 BindLambdaForTesting(
298 [&](JobDelegate* delegate) { --max_concurrency; }),
299 BindLambdaForTesting([&](size_t worker_count) -> size_t {
300 return max_concurrency + worker_count;
301 }),
302 &pooled_task_runner_delegate_);
303
304 EXPECT_TRUE(task_source->WillJoin());
305 // Once the worker_task runs, |worker_count| should drop to 0 and the job
306 // should finish.
307 EXPECT_FALSE(task_source->RunJoinTask());
308 EXPECT_EQ(0U, max_concurrency);
309 }
310
311 // Verifies that WillJoin() doesn't allow a joining thread to contribute
312 // after Cancel() is called.
TEST_F(ThreadPoolJobTaskSourceTest,CancelJoinTask)313 TEST_F(ThreadPoolJobTaskSourceTest, CancelJoinTask) {
314 auto job_task = base::MakeRefCounted<test::MockJobTask>(
315 DoNothing(), /* num_tasks_to_run */ 2);
316 scoped_refptr<JobTaskSource> task_source =
317 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
318
319 task_source->Cancel();
320 EXPECT_FALSE(task_source->WillJoin());
321 }
322
323 // Verifies that RunJoinTask() doesn't allow a joining thread to contribute
324 // after Cancel() is called.
TEST_F(ThreadPoolJobTaskSourceTest,JoinCancelTask)325 TEST_F(ThreadPoolJobTaskSourceTest, JoinCancelTask) {
326 auto job_task = base::MakeRefCounted<test::MockJobTask>(
327 DoNothing(), /* num_tasks_to_run */ 2);
328 scoped_refptr<JobTaskSource> task_source =
329 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
330
331 EXPECT_TRUE(task_source->WillJoin());
332 task_source->Cancel();
333 EXPECT_FALSE(task_source->RunJoinTask());
334 }
335
336 // Verifies that the join task can run in parallel with worker tasks up to
337 // |max_concurrency|.
TEST_F(ThreadPoolJobTaskSourceTest,RunJoinTaskInParallel)338 TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTaskInParallel) {
339 auto job_task = base::MakeRefCounted<test::MockJobTask>(
340 DoNothing(), /* num_tasks_to_run */ 2);
341 scoped_refptr<JobTaskSource> task_source =
342 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
343
344 auto registered_task_source =
345 RegisteredTaskSource::CreateForTesting(task_source);
346 EXPECT_EQ(registered_task_source.WillRunTask(),
347 TaskSource::RunStatus::kAllowedNotSaturated);
348 auto worker_task = registered_task_source.TakeTask();
349
350 EXPECT_TRUE(task_source->WillJoin());
351 EXPECT_TRUE(task_source->IsActive());
352
353 std::move(worker_task.task).Run();
354 EXPECT_FALSE(registered_task_source.DidProcessTask());
355
356 EXPECT_FALSE(task_source->RunJoinTask());
357 EXPECT_FALSE(task_source->IsActive());
358 }
359
360 // Verifies that a call to NotifyConcurrencyIncrease() calls the delegate
361 // and allows to run additional tasks.
TEST_F(ThreadPoolJobTaskSourceTest,NotifyConcurrencyIncrease)362 TEST_F(ThreadPoolJobTaskSourceTest, NotifyConcurrencyIncrease) {
363 auto job_task = base::MakeRefCounted<test::MockJobTask>(
364 DoNothing(), /* num_tasks_to_run */ 1);
365 scoped_refptr<JobTaskSource> task_source =
366 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
367
368 auto registered_task_source_a =
369 RegisteredTaskSource::CreateForTesting(task_source);
370 EXPECT_EQ(registered_task_source_a.WillRunTask(),
371 TaskSource::RunStatus::kAllowedSaturated);
372 auto task_a = registered_task_source_a.TakeTask();
373 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
374 TaskSource::RunStatus::kDisallowed);
375
376 job_task->SetNumTasksToRun(2);
377 EXPECT_CALL(pooled_task_runner_delegate_, EnqueueJobTaskSource(_)).Times(1);
378 task_source->NotifyConcurrencyIncrease();
379
380 auto registered_task_source_b =
381 RegisteredTaskSource::CreateForTesting(task_source);
382 // WillRunTask() should return a valid RunStatus because max concurrency was
383 // increased to 2.
384 EXPECT_EQ(registered_task_source_b.WillRunTask(),
385 TaskSource::RunStatus::kAllowedSaturated);
386 auto task_b = registered_task_source_b.TakeTask();
387 EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
388 TaskSource::RunStatus::kDisallowed);
389
390 std::move(task_a.task).Run();
391 EXPECT_FALSE(registered_task_source_a.DidProcessTask());
392
393 std::move(task_b.task).Run();
394 EXPECT_FALSE(registered_task_source_b.DidProcessTask());
395 }
396
397 // Verifies that ShouldYield() calls the delegate.
TEST_F(ThreadPoolJobTaskSourceTest,ShouldYield)398 TEST_F(ThreadPoolJobTaskSourceTest, ShouldYield) {
399 auto job_task = base::MakeRefCounted<test::MockJobTask>(
400 BindLambdaForTesting([](JobDelegate* delegate) {
401 // As set up below, the mock will return false once and true the second
402 // time.
403 EXPECT_FALSE(delegate->ShouldYield());
404 EXPECT_TRUE(delegate->ShouldYield());
405 }),
406 /* num_tasks_to_run */ 1);
407 scoped_refptr<JobTaskSource> task_source =
408 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
409
410 auto registered_task_source =
411 RegisteredTaskSource::CreateForTesting(task_source);
412 ASSERT_EQ(registered_task_source.WillRunTask(),
413 TaskSource::RunStatus::kAllowedSaturated);
414
415 auto task = registered_task_source.TakeTask();
416
417 EXPECT_CALL(pooled_task_runner_delegate_, ShouldYield(_))
418 .Times(2)
419 .WillOnce(Return(false))
420 .WillOnce(Return(true));
421
422 std::move(task.task).Run();
423 EXPECT_FALSE(registered_task_source.DidProcessTask());
424 }
425
426 // Verifies that max concurrency is allowed to stagnate when ShouldYield returns
427 // true.
TEST_F(ThreadPoolJobTaskSourceTest,MaxConcurrencyStagnateIfShouldYield)428 TEST_F(ThreadPoolJobTaskSourceTest, MaxConcurrencyStagnateIfShouldYield) {
429 scoped_refptr<JobTaskSource> task_source =
430 base::MakeRefCounted<JobTaskSource>(
431 FROM_HERE, TaskTraits(), BindRepeating([](JobDelegate* delegate) {
432 // As set up below, the mock will return true once.
433 ASSERT_TRUE(delegate->ShouldYield());
434 }),
435 BindRepeating([](size_t /*worker_count*/) -> size_t {
436 return 1; // max concurrency is always 1.
437 }),
438 &pooled_task_runner_delegate_);
439
440 EXPECT_CALL(pooled_task_runner_delegate_, ShouldYield(_))
441 .WillOnce(Return(true));
442
443 auto registered_task_source =
444 RegisteredTaskSource::CreateForTesting(task_source);
445 ASSERT_EQ(registered_task_source.WillRunTask(),
446 TaskSource::RunStatus::kAllowedSaturated);
447 auto task = registered_task_source.TakeTask();
448
449 // Running the task should not fail even though max concurrency remained at 1,
450 // since ShouldYield() returned true.
451 std::move(task.task).Run();
452 registered_task_source.DidProcessTask();
453 }
454
TEST_F(ThreadPoolJobTaskSourceTest,InvalidTakeTask)455 TEST_F(ThreadPoolJobTaskSourceTest, InvalidTakeTask) {
456 auto job_task =
457 base::MakeRefCounted<test::MockJobTask>(DoNothing(),
458 /* num_tasks_to_run */ 1);
459 scoped_refptr<JobTaskSource> task_source =
460 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
461
462 auto registered_task_source_a =
463 RegisteredTaskSource::CreateForTesting(task_source);
464 EXPECT_EQ(registered_task_source_a.WillRunTask(),
465 TaskSource::RunStatus::kAllowedSaturated);
466
467 auto registered_task_source_b =
468 RegisteredTaskSource::CreateForTesting(task_source);
469 EXPECT_EQ(registered_task_source_b.WillRunTask(),
470 TaskSource::RunStatus::kDisallowed);
471
472 // Can not be called with an invalid RunStatus.
473 EXPECT_DCHECK_DEATH({ auto task = registered_task_source_b.TakeTask(); });
474
475 auto task = registered_task_source_a.TakeTask();
476 registered_task_source_a.DidProcessTask();
477 }
478
TEST_F(ThreadPoolJobTaskSourceTest,InvalidDidProcessTask)479 TEST_F(ThreadPoolJobTaskSourceTest, InvalidDidProcessTask) {
480 auto job_task =
481 base::MakeRefCounted<test::MockJobTask>(DoNothing(),
482 /* num_tasks_to_run */ 1);
483 scoped_refptr<JobTaskSource> task_source =
484 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
485
486 auto registered_task_source =
487 RegisteredTaskSource::CreateForTesting(task_source);
488
489 // Can not be called before WillRunTask().
490 EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask());
491 }
492
TEST_F(ThreadPoolJobTaskSourceTest,AcquireTaskId)493 TEST_F(ThreadPoolJobTaskSourceTest, AcquireTaskId) {
494 auto job_task =
495 base::MakeRefCounted<test::MockJobTask>(DoNothing(),
496 /* num_tasks_to_run */ 4);
497 scoped_refptr<JobTaskSource> task_source =
498 job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
499
500 EXPECT_EQ(0U, task_source->AcquireTaskId());
501 EXPECT_EQ(1U, task_source->AcquireTaskId());
502 EXPECT_EQ(2U, task_source->AcquireTaskId());
503 EXPECT_EQ(3U, task_source->AcquireTaskId());
504 EXPECT_EQ(4U, task_source->AcquireTaskId());
505 task_source->ReleaseTaskId(1);
506 task_source->ReleaseTaskId(3);
507 EXPECT_EQ(1U, task_source->AcquireTaskId());
508 EXPECT_EQ(3U, task_source->AcquireTaskId());
509 EXPECT_EQ(5U, task_source->AcquireTaskId());
510 }
511
512 // Verifies that task id is released after worker_task returns.
TEST_F(ThreadPoolJobTaskSourceTest,GetTaskId)513 TEST_F(ThreadPoolJobTaskSourceTest, GetTaskId) {
514 auto task_source = MakeRefCounted<JobTaskSource>(
515 FROM_HERE, TaskTraits{}, BindRepeating([](JobDelegate* delegate) {
516 // Confirm that task id 0 is reused on the second run.
517 EXPECT_EQ(0U, delegate->GetTaskId());
518
519 // Allow running the task again.
520 delegate->NotifyConcurrencyIncrease();
521 }),
522 BindRepeating([](size_t /*worker_count*/) -> size_t { return 1; }),
523 &pooled_task_runner_delegate_);
524
525 auto registered_task_source =
526 RegisteredTaskSource::CreateForTesting(task_source);
527
528 // Run the worker_task twice.
529 ASSERT_EQ(registered_task_source.WillRunTask(),
530 TaskSource::RunStatus::kAllowedSaturated);
531 auto task1 = registered_task_source.TakeTask();
532 std::move(task1.task).Run();
533 registered_task_source.DidProcessTask();
534
535 ASSERT_EQ(registered_task_source.WillRunTask(),
536 TaskSource::RunStatus::kAllowedSaturated);
537 auto task2 = registered_task_source.TakeTask();
538 std::move(task2.task).Run();
539 registered_task_source.DidProcessTask();
540 }
541
542 } // namespace internal
543 } // namespace base
544