blob: 603bf73cd7fa26645a0677452ea7189ad363f18d [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/task_scheduler/task_tracker.h"
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_token.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_scheduler/scheduler_lock.h"
#include "base/task_scheduler/sequence.h"
#include "base/task_scheduler/task.h"
#include "base/task_scheduler/task_traits.h"
#include "base/task_scheduler/test_utils.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace internal {
namespace {
constexpr size_t kLoadTestNumIterations = 100;
scoped_refptr<Sequence> CreateSequenceWithTask(std::unique_ptr<Task> task) {
scoped_refptr<Sequence> sequence(new Sequence);
sequence->PushTask(std::move(task));
return sequence;
}
// Calls TaskTracker::Shutdown() asynchronously.
class ThreadCallingShutdown : public SimpleThread {
public:
explicit ThreadCallingShutdown(TaskTracker* tracker)
: SimpleThread("ThreadCallingShutdown"),
tracker_(tracker),
has_returned_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED) {}
// Returns true once the async call to Shutdown() has returned.
bool has_returned() { return has_returned_.IsSignaled(); }
private:
void Run() override {
tracker_->Shutdown();
has_returned_.Signal();
}
TaskTracker* const tracker_;
WaitableEvent has_returned_;
DISALLOW_COPY_AND_ASSIGN(ThreadCallingShutdown);
};
class ThreadPostingAndRunningTask : public SimpleThread {
public:
enum class Action {
WILL_POST,
RUN,
WILL_POST_AND_RUN,
};
ThreadPostingAndRunningTask(TaskTracker* tracker,
scoped_refptr<Sequence> sequence,
Action action,
bool expect_post_succeeds)
: SimpleThread("ThreadPostingAndRunningTask"),
tracker_(tracker),
sequence_(std::move(sequence)),
action_(action),
expect_post_succeeds_(expect_post_succeeds) {}
private:
void Run() override {
bool post_succeeded = true;
if (action_ == Action::WILL_POST || action_ == Action::WILL_POST_AND_RUN) {
post_succeeded = tracker_->WillPostTask(sequence_->PeekTask());
EXPECT_EQ(expect_post_succeeds_, post_succeeded);
}
if (post_succeeded &&
(action_ == Action::RUN || action_ == Action::WILL_POST_AND_RUN)) {
tracker_->RunNextTaskInSequence(sequence_.get());
}
}
TaskTracker* const tracker_;
const scoped_refptr<Sequence> sequence_;
const Action action_;
const bool expect_post_succeeds_;
DISALLOW_COPY_AND_ASSIGN(ThreadPostingAndRunningTask);
};
class ScopedSetSingletonAllowed {
public:
ScopedSetSingletonAllowed(bool singleton_allowed)
: previous_value_(
ThreadRestrictions::SetSingletonAllowed(singleton_allowed)) {}
~ScopedSetSingletonAllowed() {
ThreadRestrictions::SetSingletonAllowed(previous_value_);
}
private:
const bool previous_value_;
};
class TaskSchedulerTaskTrackerTest
: public testing::TestWithParam<TaskShutdownBehavior> {
protected:
TaskSchedulerTaskTrackerTest() = default;
// Creates a task with |shutdown_behavior|.
std::unique_ptr<Task> CreateTask(TaskShutdownBehavior shutdown_behavior) {
return WrapUnique(new Task(
FROM_HERE,
Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)),
TaskTraits().WithShutdownBehavior(shutdown_behavior), TimeDelta()));
}
// Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown()
// method has been entered on the new thread, but it hasn't necessarily
// returned.
void CallShutdownAsync() {
ASSERT_FALSE(thread_calling_shutdown_);
thread_calling_shutdown_.reset(new ThreadCallingShutdown(&tracker_));
thread_calling_shutdown_->Start();
while (!tracker_.HasShutdownStarted())
PlatformThread::YieldCurrentThread();
}
void WaitForAsyncIsShutdownComplete() {
ASSERT_TRUE(thread_calling_shutdown_);
thread_calling_shutdown_->Join();
EXPECT_TRUE(thread_calling_shutdown_->has_returned());
EXPECT_TRUE(tracker_.IsShutdownComplete());
}
void VerifyAsyncShutdownInProgress() {
ASSERT_TRUE(thread_calling_shutdown_);
EXPECT_FALSE(thread_calling_shutdown_->has_returned());
EXPECT_TRUE(tracker_.HasShutdownStarted());
EXPECT_FALSE(tracker_.IsShutdownComplete());
}
size_t NumTasksExecuted() {
AutoSchedulerLock auto_lock(lock_);
return num_tasks_executed_;
}
TaskTracker tracker_;
private:
void RunTaskCallback() {
AutoSchedulerLock auto_lock(lock_);
++num_tasks_executed_;
}
std::unique_ptr<ThreadCallingShutdown> thread_calling_shutdown_;
// Synchronizes accesses to |num_tasks_executed_|.
SchedulerLock lock_;
size_t num_tasks_executed_ = 0;
DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest);
};
#define WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED() \
do { \
SCOPED_TRACE(""); \
WaitForAsyncIsShutdownComplete(); \
} while (false)
#define VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS() \
do { \
SCOPED_TRACE(""); \
VerifyAsyncShutdownInProgress(); \
} while (false)
} // namespace
TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunBeforeShutdown) {
std::unique_ptr<Task> task(CreateTask(GetParam()));
// Inform |task_tracker_| that |task| will be posted.
EXPECT_TRUE(tracker_.WillPostTask(task.get()));
// Run the task.
EXPECT_EQ(0U, NumTasksExecuted());
tracker_.RunNextTaskInSequence(CreateSequenceWithTask(std::move(task)).get());
EXPECT_EQ(1U, NumTasksExecuted());
// Shutdown() shouldn't block.
tracker_.Shutdown();
}
TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunLongTaskBeforeShutdown) {
// Create a task that will block until |event| is signaled.
WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED);
std::unique_ptr<Task> blocked_task(
new Task(FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
// Inform |task_tracker_| that |blocked_task| will be posted.
EXPECT_TRUE(tracker_.WillPostTask(blocked_task.get()));
// Run the task asynchronouly.
ThreadPostingAndRunningTask thread_running_task(
&tracker_, CreateSequenceWithTask(std::move(blocked_task)),
ThreadPostingAndRunningTask::Action::RUN, false);
thread_running_task.Start();
// Initiate shutdown while the task is running.
CallShutdownAsync();
if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) {
// Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress.
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
} else {
// Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress.
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
}
// Unblock the task.
event.Signal();
thread_running_task.Join();
// Shutdown should now complete for a non CONTINUE_ON_SHUTDOWN task.
if (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
}
TEST_P(TaskSchedulerTaskTrackerTest, WillPostBeforeShutdownRunDuringShutdown) {
// Inform |task_tracker_| that a task will be posted.
std::unique_ptr<Task> task(CreateTask(GetParam()));
EXPECT_TRUE(tracker_.WillPostTask(task.get()));
// Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
// block shutdown.
std::unique_ptr<Task> block_shutdown_task(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
EXPECT_TRUE(tracker_.WillPostTask(block_shutdown_task.get()));
// Call Shutdown() asynchronously.
CallShutdownAsync();
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
// Try to run |task|. It should only run it it's BLOCK_SHUTDOWN. Otherwise it
// should be discarded.
EXPECT_EQ(0U, NumTasksExecuted());
tracker_.RunNextTaskInSequence(CreateSequenceWithTask(std::move(task)).get());
EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 1U : 0U,
NumTasksExecuted());
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
// Unblock shutdown by running the remaining BLOCK_SHUTDOWN task.
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(block_shutdown_task)).get());
EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
NumTasksExecuted());
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
}
TEST_P(TaskSchedulerTaskTrackerTest, WillPostBeforeShutdownRunAfterShutdown) {
// Inform |task_tracker_| that a task will be posted.
std::unique_ptr<Task> task(CreateTask(GetParam()));
EXPECT_TRUE(tracker_.WillPostTask(task.get()));
// Call Shutdown() asynchronously.
CallShutdownAsync();
EXPECT_EQ(0U, NumTasksExecuted());
if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
// Run the task to unblock shutdown.
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(task)).get());
EXPECT_EQ(1U, NumTasksExecuted());
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
// It is not possible to test running a BLOCK_SHUTDOWN task posted before
// shutdown after shutdown because Shutdown() won't return if there are
// pending BLOCK_SHUTDOWN tasks.
} else {
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
// The task shouldn't be allowed to run after shutdown.
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(task)).get());
EXPECT_EQ(0U, NumTasksExecuted());
}
}
TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunDuringShutdown) {
// Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
// block shutdown.
std::unique_ptr<Task> block_shutdown_task(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
EXPECT_TRUE(tracker_.WillPostTask(block_shutdown_task.get()));
// Call Shutdown() asynchronously.
CallShutdownAsync();
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
// Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted.
std::unique_ptr<Task> task(CreateTask(GetParam()));
EXPECT_TRUE(tracker_.WillPostTask(task.get()));
// Run the BLOCK_SHUTDOWN task.
EXPECT_EQ(0U, NumTasksExecuted());
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(task)).get());
EXPECT_EQ(1U, NumTasksExecuted());
} else {
// It shouldn't be allowed to post a non BLOCK_SHUTDOWN task.
std::unique_ptr<Task> task(CreateTask(GetParam()));
EXPECT_FALSE(tracker_.WillPostTask(task.get()));
// Don't try to run the task, because it wasn't allowed to be posted.
}
// Unblock shutdown by running |block_shutdown_task|.
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(block_shutdown_task)).get());
EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U,
NumTasksExecuted());
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
}
TEST_P(TaskSchedulerTaskTrackerTest, WillPostAfterShutdown) {
tracker_.Shutdown();
std::unique_ptr<Task> task(CreateTask(GetParam()));
// |task_tracker_| shouldn't allow a task to be posted after shutdown.
if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
EXPECT_DCHECK_DEATH({ tracker_.WillPostTask(task.get()); }, "");
} else {
EXPECT_FALSE(tracker_.WillPostTask(task.get()));
}
}
// Verify that BLOCK_SHUTDOWN and SKIP_ON_SHUTDOWN tasks can
// AssertSingletonAllowed() but CONTINUE_ON_SHUTDOWN tasks can't.
TEST_P(TaskSchedulerTaskTrackerTest, SingletonAllowed) {
const bool can_use_singletons =
(GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN);
TaskTracker tracker;
std::unique_ptr<Task> task(
new Task(FROM_HERE, Bind(&ThreadRestrictions::AssertSingletonAllowed),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
EXPECT_TRUE(tracker.WillPostTask(task.get()));
// Set the singleton allowed bit to the opposite of what it is expected to be
// when |tracker| runs |task| to verify that |tracker| actually sets the
// correct value.
ScopedSetSingletonAllowed scoped_singleton_allowed(!can_use_singletons);
// Running the task should fail iff the task isn't allowed to use singletons.
if (can_use_singletons) {
tracker.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(task)).get());
} else {
EXPECT_DCHECK_DEATH(
{
tracker.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(task)).get());
},
"");
}
}
static void RunTaskRunnerHandleVerificationTask(
TaskTracker* tracker,
std::unique_ptr<Task> verify_task) {
// Pretend |verify_task| is posted to respect TaskTracker's contract.
EXPECT_TRUE(tracker->WillPostTask(verify_task.get()));
// Confirm that the test conditions are right (no TaskRunnerHandles set
// already).
EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
tracker->RunNextTaskInSequence(
CreateSequenceWithTask(std::move(verify_task)).get());
// TaskRunnerHandle state is reset outside of task's scope.
EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
}
static void VerifyNoTaskRunnerHandle() {
EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
}
TEST_P(TaskSchedulerTaskTrackerTest, TaskRunnerHandleIsNotSetOnParallel) {
// Create a task that will verify that TaskRunnerHandles are not set in its
// scope per no TaskRunner ref being set to it.
std::unique_ptr<Task> verify_task(
new Task(FROM_HERE, Bind(&VerifyNoTaskRunnerHandle),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
}
static void VerifySequencedTaskRunnerHandle(
const SequencedTaskRunner* expected_task_runner) {
EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
EXPECT_EQ(expected_task_runner, SequencedTaskRunnerHandle::Get());
}
TEST_P(TaskSchedulerTaskTrackerTest,
SequencedTaskRunnerHandleIsSetOnSequenced) {
scoped_refptr<SequencedTaskRunner> test_task_runner(new TestSimpleTaskRunner);
// Create a task that will verify that SequencedTaskRunnerHandle is properly
// set to |test_task_runner| in its scope per |sequenced_task_runner_ref|
// being set to it.
std::unique_ptr<Task> verify_task(
new Task(FROM_HERE, Bind(&VerifySequencedTaskRunnerHandle,
base::Unretained(test_task_runner.get())),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
verify_task->sequenced_task_runner_ref = test_task_runner;
RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
}
static void VerifyThreadTaskRunnerHandle(
const SingleThreadTaskRunner* expected_task_runner) {
EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
// SequencedTaskRunnerHandle inherits ThreadTaskRunnerHandle for thread.
EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
EXPECT_EQ(expected_task_runner, ThreadTaskRunnerHandle::Get());
}
TEST_P(TaskSchedulerTaskTrackerTest,
ThreadTaskRunnerHandleIsSetOnSingleThreaded) {
scoped_refptr<SingleThreadTaskRunner> test_task_runner(
new TestSimpleTaskRunner);
// Create a task that will verify that ThreadTaskRunnerHandle is properly set
// to |test_task_runner| in its scope per |single_thread_task_runner_ref|
// being set on it.
std::unique_ptr<Task> verify_task(
new Task(FROM_HERE, Bind(&VerifyThreadTaskRunnerHandle,
base::Unretained(test_task_runner.get())),
TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta()));
verify_task->single_thread_task_runner_ref = test_task_runner;
RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task));
}
INSTANTIATE_TEST_CASE_P(
ContinueOnShutdown,
TaskSchedulerTaskTrackerTest,
::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
INSTANTIATE_TEST_CASE_P(
SkipOnShutdown,
TaskSchedulerTaskTrackerTest,
::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN));
INSTANTIATE_TEST_CASE_P(
BlockShutdown,
TaskSchedulerTaskTrackerTest,
::testing::Values(TaskShutdownBehavior::BLOCK_SHUTDOWN));
namespace {
void ExpectSequenceToken(SequenceToken sequence_token) {
EXPECT_EQ(sequence_token, SequenceToken::GetForCurrentThread());
}
} // namespace
// Verify that SequenceToken::GetForCurrentThread() returns the Sequence's token
// when a Task runs.
TEST_F(TaskSchedulerTaskTrackerTest, CurrentSequenceToken) {
scoped_refptr<Sequence> sequence(new Sequence);
sequence->PushTask(WrapUnique(
new Task(FROM_HERE, Bind(&ExpectSequenceToken, sequence->token()),
TaskTraits(), TimeDelta())));
tracker_.WillPostTask(sequence->PeekTask());
EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
tracker_.RunNextTaskInSequence(sequence.get());
EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid());
}
TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunBeforeShutdown) {
// Post and run tasks asynchronously.
std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true)));
threads.back()->Start();
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true)));
threads.back()->Start();
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true)));
threads.back()->Start();
}
for (const auto& thread : threads)
thread->Join();
// Expect all tasks to be executed.
EXPECT_EQ(kLoadTestNumIterations * 3, NumTasksExecuted());
// Should return immediately because no tasks are blocking shutdown.
tracker_.Shutdown();
}
TEST_F(TaskSchedulerTaskTrackerTest,
LoadWillPostBeforeShutdownAndRunDuringShutdown) {
// Post tasks asynchronously.
std::vector<scoped_refptr<Sequence>> sequences;
std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> post_threads;
for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
sequences.push_back(CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)));
post_threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, sequences.back(),
ThreadPostingAndRunningTask::Action::WILL_POST, true)));
post_threads.back()->Start();
sequences.push_back(CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)));
post_threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, sequences.back(),
ThreadPostingAndRunningTask::Action::WILL_POST, true)));
post_threads.back()->Start();
sequences.push_back(CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)));
post_threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, sequences.back(),
ThreadPostingAndRunningTask::Action::WILL_POST, true)));
post_threads.back()->Start();
}
for (const auto& thread : post_threads)
thread->Join();
// Call Shutdown() asynchronously.
CallShutdownAsync();
// Run tasks asynchronously.
std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> run_threads;
for (const auto& sequence : sequences) {
run_threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, sequence, ThreadPostingAndRunningTask::Action::RUN, false)));
run_threads.back()->Start();
}
for (const auto& thread : run_threads)
thread->Join();
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
// Expect BLOCK_SHUTDOWN tasks to have been executed.
EXPECT_EQ(kLoadTestNumIterations, NumTasksExecuted());
}
TEST_F(TaskSchedulerTaskTrackerTest, LoadWillPostAndRunDuringShutdown) {
// Inform |task_tracker_| that a BLOCK_SHUTDOWN task will be posted just to
// block shutdown.
std::unique_ptr<Task> block_shutdown_task(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN));
EXPECT_TRUE(tracker_.WillPostTask(block_shutdown_task.get()));
// Call Shutdown() asynchronously.
CallShutdownAsync();
// Post and run tasks asynchronously.
std::vector<std::unique_ptr<ThreadPostingAndRunningTask>> threads;
for (size_t i = 0; i < kLoadTestNumIterations; ++i) {
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, false)));
threads.back()->Start();
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, false)));
threads.back()->Start();
threads.push_back(WrapUnique(new ThreadPostingAndRunningTask(
&tracker_, CreateSequenceWithTask(
CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)),
ThreadPostingAndRunningTask::Action::WILL_POST_AND_RUN, true)));
threads.back()->Start();
}
for (const auto& thread : threads)
thread->Join();
// Expect BLOCK_SHUTDOWN tasks to have been executed.
EXPECT_EQ(kLoadTestNumIterations, NumTasksExecuted());
// Shutdown() shouldn't return before |block_shutdown_task| is executed.
VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS();
// Unblock shutdown by running |block_shutdown_task|.
tracker_.RunNextTaskInSequence(
CreateSequenceWithTask(std::move(block_shutdown_task)).get());
EXPECT_EQ(kLoadTestNumIterations + 1, NumTasksExecuted());
WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED();
}
} // namespace internal
} // namespace base