#include "base/task/thread_pool/sequence.h"
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/test/gtest_util.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
class MockTask {
public:
MOCK_METHOD0(Run, void());
};
Task CreateTask(MockTask* mock_task, TimeTicks now = TimeTicks::Now()) {
return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), now,
TimeDelta());
}
Task CreateDelayedTask(MockTask* mock_task,
TimeDelta delay,
TimeTicks now = TimeTicks::Now()) {
return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), now,
delay);
}
void ExpectMockTask(MockTask* mock_task, Task* task) {
EXPECT_CALL(*mock_task, Run());
std::move(task->task).Run();
testing::Mock::VerifyAndClear(mock_task);
}
}
TEST(ThreadPoolSequenceTest, PushTakeRemove) {
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
testing::StrictMock<MockTask> mock_task_c;
testing::StrictMock<MockTask> mock_task_d;
testing::StrictMock<MockTask> mock_task_e;
scoped_refptr<Sequence> sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
EXPECT_TRUE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a));
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b));
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_c));
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_d));
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
std::optional<Task> task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_a, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(),
&sequence_transaction));
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_b, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(),
&sequence_transaction));
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_c, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(),
&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_e));
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_d, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(),
&sequence_transaction));
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_e, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
EXPECT_FALSE(sequence->is_immediate_for_testing());
EXPECT_TRUE(sequence->IsEmptyForTesting());
}
TEST(ThreadPoolSequenceTest, GetSortKeyBestEffort) {
Task best_effort_task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta());
scoped_refptr<Sequence> best_effort_sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction best_effort_sequence_transaction(
best_effort_sequence->BeginTransaction());
best_effort_sequence_transaction.WillPushImmediateTask();
best_effort_sequence_transaction.PushImmediateTask(
std::move(best_effort_task));
const TaskSourceSortKey best_effort_sort_key =
best_effort_sequence->GetSortKey();
auto best_effort_registered_task_source =
RegisteredTaskSource::CreateForTesting(best_effort_sequence);
best_effort_registered_task_source.WillRunTask();
auto take_best_effort_task = best_effort_registered_task_source.TakeTask(
&best_effort_sequence_transaction);
EXPECT_EQ(TaskPriority::BEST_EFFORT, best_effort_sort_key.priority());
EXPECT_EQ(take_best_effort_task.queue_time,
best_effort_sort_key.ready_time());
best_effort_registered_task_source.DidProcessTask(
&best_effort_sequence_transaction);
}
TEST(ThreadPoolSequenceTest, GetSortKeyForeground) {
Task foreground_task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta());
scoped_refptr<Sequence> foreground_sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::USER_VISIBLE), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction foreground_sequence_transaction(
foreground_sequence->BeginTransaction());
foreground_sequence_transaction.WillPushImmediateTask();
foreground_sequence_transaction.PushImmediateTask(std::move(foreground_task));
const TaskSourceSortKey foreground_sort_key =
foreground_sequence->GetSortKey();
auto foreground_registered_task_source =
RegisteredTaskSource::CreateForTesting(foreground_sequence);
foreground_registered_task_source.WillRunTask();
auto take_foreground_task = foreground_registered_task_source.TakeTask(
&foreground_sequence_transaction);
EXPECT_EQ(TaskPriority::USER_VISIBLE, foreground_sort_key.priority());
EXPECT_EQ(take_foreground_task.queue_time, foreground_sort_key.ready_time());
foreground_registered_task_source.DidProcessTask(
&foreground_sequence_transaction);
}
TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutWillRunTask) {
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
EXPECT_TRUE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta()));
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
EXPECT_DCHECK_DEATH(
{ registered_task_source.DidProcessTask(&sequence_transaction); });
}
TEST(ThreadPoolSequenceTest, TakeEmptyFrontSlot) {
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
sequence_transaction.WillPushImmediateTask();
sequence_transaction.PushImmediateTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta()));
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
{
registered_task_source.WillRunTask();
IgnoreResult(registered_task_source.TakeTask(&sequence_transaction));
registered_task_source.DidProcessTask(&sequence_transaction);
}
EXPECT_DCHECK_DEATH({
registered_task_source.WillRunTask();
auto task = registered_task_source.TakeTask(&sequence_transaction);
});
}
TEST(ThreadPoolSequenceTest, TakeEmptySequence) {
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
EXPECT_DCHECK_DEATH({
registered_task_source.WillRunTask();
auto task = registered_task_source.TakeTask();
});
}
TEST(ThreadPoolSequenceTest, SequenceHasWorker) {
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
scoped_refptr<Sequence> sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
EXPECT_TRUE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a));
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
std::optional<Task> task_a =
registered_task_source.TakeTask(&sequence_transaction);
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b));
EXPECT_TRUE(sequence->has_worker_for_testing());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(),
&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
registered_task_source.WillRunTask();
std::optional<Task> task_b =
registered_task_source.TakeTask(&sequence_transaction);
EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
EXPECT_FALSE(sequence->is_immediate_for_testing());
EXPECT_TRUE(sequence->IsEmptyForTesting());
}
TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) {
TimeTicks now = TimeTicks::Now();
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
testing::StrictMock<MockTask> mock_task_c;
testing::StrictMock<MockTask> mock_task_d;
scoped_refptr<Sequence> sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now);
EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_a)));
auto delayed_task_b = CreateDelayedTask(&mock_task_b, Milliseconds(10), now);
EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_b)));
EXPECT_FALSE(sequence->is_immediate_for_testing());
now += Milliseconds(15);
EXPECT_TRUE(sequence->OnBecomeReady());
EXPECT_TRUE(sequence->is_immediate_for_testing());
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
std::optional<Task> task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_b, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_FALSE(
registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_FALSE(sequence->is_immediate_for_testing());
auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(1), now);
EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_c)));
auto delayed_task_d = CreateDelayedTask(&mock_task_d, Milliseconds(1), now);
EXPECT_FALSE(sequence_transaction.PushDelayedTask(std::move(delayed_task_d)));
now += Milliseconds(2);
EXPECT_TRUE(registered_task_source->OnBecomeReady());
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_c, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_TRUE(sequence->is_immediate_for_testing());
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_d, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
now += Milliseconds(10);
EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_TRUE(sequence->is_immediate_for_testing());
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_a, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
EXPECT_FALSE(sequence->is_immediate_for_testing());
EXPECT_TRUE(sequence->IsEmptyForTesting());
}
TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) {
TimeTicks now = TimeTicks::Now();
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
testing::StrictMock<MockTask> mock_task_c;
testing::StrictMock<MockTask> mock_task_d;
scoped_refptr<Sequence> sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now);
EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_a)));
EXPECT_FALSE(sequence->is_immediate_for_testing());
auto task_b = CreateTask(&mock_task_b, now);
EXPECT_TRUE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(std::move(task_b));
EXPECT_TRUE(sequence->is_immediate_for_testing());
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
std::optional<Task> task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_b, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
now += Milliseconds(21);
EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_TRUE(sequence->is_immediate_for_testing());
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(5), now);
EXPECT_FALSE(sequence_transaction.PushDelayedTask(std::move(delayed_task_c)));
EXPECT_TRUE(sequence->has_worker_for_testing());
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_a, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
now += Milliseconds(2);
EXPECT_FALSE(
registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_FALSE(sequence->is_immediate_for_testing());
now += Milliseconds(4);
EXPECT_TRUE(registered_task_source->OnBecomeReady());
EXPECT_TRUE(sequence->is_immediate_for_testing());
auto task_d = CreateTask(&mock_task_d, now);
EXPECT_FALSE(sequence_transaction.WillPushImmediateTask());
sequence_transaction.PushImmediateTask(std::move(task_d));
EXPECT_TRUE(sequence->is_immediate_for_testing());
registered_task_source.WillRunTask();
EXPECT_TRUE(sequence->has_worker_for_testing());
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_c, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction));
EXPECT_TRUE(sequence->is_immediate_for_testing());
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_d, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction));
EXPECT_FALSE(sequence->has_worker_for_testing());
EXPECT_FALSE(sequence->is_immediate_for_testing());
}
TEST(ThreadPoolSequenceTest, TestPushDelayedTaskMethodUsage) {
testing::StrictMock<MockTask> mock_task_a;
scoped_refptr<Sequence> sequence =
MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
auto task_a = CreateTask(&mock_task_a);
EXPECT_DCHECK_DEATH(
{ sequence_transaction.PushDelayedTask(std::move(task_a)); });
}
TEST(ThreadPoolSequenceTest, GetDelayedSortKeyMixedtasks) {
TimeTicks now = TimeTicks::Now();
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
EXPECT_TRUE(sequence_transaction.PushDelayedTask(
CreateDelayedTask(&mock_task_a, Milliseconds(10), now)));
const TimeTicks sort_key_1 = sequence->GetDelayedSortKey();
now += Milliseconds(11);
auto immediate_task = CreateTask(&mock_task_b, now);
sequence_transaction.WillPushImmediateTask();
sequence_transaction.PushImmediateTask(std::move(immediate_task));
const TimeTicks sort_key_2 = sequence->GetDelayedSortKey();
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
std::optional<Task> take_delayed_task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_a, &take_delayed_task.value());
EXPECT_FALSE(take_delayed_task->queue_time.is_null());
registered_task_source.DidProcessTask(&sequence_transaction);
registered_task_source.WillReEnqueue(now, &sequence_transaction);
EXPECT_EQ(take_delayed_task->latest_delayed_run_time(), sort_key_1);
EXPECT_EQ(sort_key_1, sort_key_2);
const TimeTicks sort_key_3 = sequence->GetDelayedSortKey();
registered_task_source.WillRunTask();
std::optional<Task> take_immediate_task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_b, &take_immediate_task.value());
EXPECT_FALSE(take_immediate_task->queue_time.is_null());
EXPECT_EQ(take_immediate_task->queue_time, sort_key_3);
registered_task_source.DidProcessTask(&sequence_transaction);
}
TEST(ThreadPoolSequenceTest, GetDelayedSortKeyDelayedtasks) {
TimeTicks now = TimeTicks::Now();
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
sequence_transaction.PushDelayedTask(
CreateDelayedTask(&mock_task_a, Milliseconds(15), now));
const TimeTicks sort_key_1 = sequence->GetDelayedSortKey();
sequence_transaction.PushDelayedTask(
CreateDelayedTask(&mock_task_b, Milliseconds(10), now));
const TimeTicks sort_key_2 = sequence->GetDelayedSortKey();
now += Milliseconds(11);
sequence->OnBecomeReady();
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
registered_task_source.WillRunTask();
std::optional<Task> task =
registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_b, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_EQ(task->latest_delayed_run_time(), sort_key_2);
now += Milliseconds(5);
registered_task_source.DidProcessTask(&sequence_transaction);
registered_task_source.WillReEnqueue(now, &sequence_transaction);
registered_task_source.WillRunTask();
task = registered_task_source.TakeTask(&sequence_transaction);
ExpectMockTask(&mock_task_a, &task.value());
EXPECT_FALSE(task->queue_time.is_null());
EXPECT_EQ(task->latest_delayed_run_time(), sort_key_1);
registered_task_source.DidProcessTask(&sequence_transaction);
}
}