#include "base/task/thread_pool/thread_pool_impl.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "base/base_switches.h"
#include "base/cfi_buildflags.h"
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/debug/stack_trace.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/system/sys_info.h"
#include "base/task/task_features.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool/environment_config.h"
#include "base/task/thread_pool/test_task_factory.h"
#include "base/task/thread_pool/test_utils.h"
#include "base/task/thread_pool/worker_thread_observer.h"
#include "base/task/updateable_sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/test/test_waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_POSIX)
#include <unistd.h>
#include "base/debug/leak_annotations.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_util.h"
#include "base/posix/eintr_wrapper.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/win/com_init_util.h"
#endif
namespace base::internal {
namespace {
constexpr size_t kMaxNumForegroundThreads = 4;
constexpr size_t kMaxNumUtilityThreads = 2;
struct TraitsExecutionModePair {
TraitsExecutionModePair(const TaskTraits& traits,
TaskSourceExecutionMode execution_mode)
: traits(traits), execution_mode(execution_mode) {}
TaskTraits traits;
TaskSourceExecutionMode execution_mode;
};
bool TraitsSupportBackgroundThreadType(const TaskTraits& traits) {
return traits.priority() == TaskPriority::BEST_EFFORT &&
traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND &&
CanUseBackgroundThreadTypeForWorkerThread();
}
bool TraitsSupportUtilityThreadType(const TaskTraits& traits) {
return traits.priority() <= TaskPriority::USER_VISIBLE &&
traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND &&
CanUseUtilityThreadTypeForWorkerThread();
}
void VerifyTaskEnvironment(const TaskTraits& traits,
bool use_resource_efficient_group) {
const std::string thread_name(PlatformThread::GetName());
const bool is_single_threaded =
(thread_name.find("SingleThread") != std::string::npos);
const bool expect_background_thread_type =
TraitsSupportBackgroundThreadType(traits);
const bool expect_utility_thread_type =
!TraitsSupportBackgroundThreadType(traits) &&
TraitsSupportUtilityThreadType(traits) && use_resource_efficient_group;
EXPECT_EQ(expect_background_thread_type ? ThreadType::kBackground
: expect_utility_thread_type ? ThreadType::kUtility
: ThreadType::kDefault,
PlatformThread::GetCurrentThreadType());
if (traits.may_block()) {
AssertBlockingAllowed();
} else {
AssertBlockingDisallowedForTesting();
}
EXPECT_THAT(thread_name, ::testing::HasSubstr("ThreadPool"));
EXPECT_THAT(thread_name, ::testing::HasSubstr(
expect_background_thread_type ? "Background"
: expect_utility_thread_type ? "Utility"
: "Foreground"));
if (is_single_threaded) {
if (traits.may_block()) {
EXPECT_THAT(thread_name, ::testing::HasSubstr("Blocking"));
} else {
EXPECT_THAT(thread_name,
::testing::Not(::testing::HasSubstr("Blocking")));
}
} else {
EXPECT_THAT(thread_name, ::testing::Not(::testing::HasSubstr("Blocking")));
}
}
void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits,
bool use_resource_efficient_group,
TestWaitableEvent* event) {
DCHECK(event);
VerifyTaskEnvironment(traits, use_resource_efficient_group);
event->Signal();
}
void VerifyTimeAndTaskEnvironmentAndSignalEvent(
const TaskTraits& traits,
bool use_resource_efficient_group,
TimeTicks expected_time,
TestWaitableEvent* event) {
DCHECK(event);
EXPECT_LE(expected_time, TimeTicks::Now());
VerifyTaskEnvironment(traits, use_resource_efficient_group);
event->Signal();
}
void VerifyOrderAndTaskEnvironmentAndSignalEvent(
const TaskTraits& traits,
bool use_resource_efficient_group,
TestWaitableEvent* expected_previous_event,
TestWaitableEvent* event) {
DCHECK(event);
if (expected_previous_event) {
EXPECT_TRUE(expected_previous_event->IsSignaled());
}
VerifyTaskEnvironment(traits, use_resource_efficient_group);
event->Signal();
}
scoped_refptr<TaskRunner> CreateTaskRunnerAndExecutionMode(
ThreadPoolImpl* thread_pool,
const TaskTraits& traits,
TaskSourceExecutionMode execution_mode,
SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
SingleThreadTaskRunnerThreadMode::SHARED) {
switch (execution_mode) {
case TaskSourceExecutionMode::kParallel:
return thread_pool->CreateTaskRunner(traits);
case TaskSourceExecutionMode::kSequenced:
return thread_pool->CreateSequencedTaskRunner(traits);
case TaskSourceExecutionMode::kSingleThread: {
return thread_pool->CreateSingleThreadTaskRunner(
traits, default_single_thread_task_runner_mode);
}
case TaskSourceExecutionMode::kJob:
break;
}
ADD_FAILURE() << "Unknown ExecutionMode";
return nullptr;
}
class ThreadPostingTasks : public SimpleThread {
public:
ThreadPostingTasks(ThreadPoolImpl* thread_pool,
const TaskTraits& traits,
bool use_resource_efficient_group,
TaskSourceExecutionMode execution_mode)
: SimpleThread("ThreadPostingTasks"),
traits_(traits),
use_resource_efficient_group_(use_resource_efficient_group),
factory_(CreateTaskRunnerAndExecutionMode(thread_pool,
traits,
execution_mode),
execution_mode) {}
ThreadPostingTasks(const ThreadPostingTasks&) = delete;
ThreadPostingTasks& operator=(const ThreadPostingTasks&) = delete;
void WaitForAllTasksToRun() { factory_.WaitForAllTasksToRun(); }
private:
void Run() override {
const size_t kNumTasksPerThread = 150;
for (size_t i = 0; i < kNumTasksPerThread; ++i) {
factory_.PostTask(test::TestTaskFactory::PostNestedTask::NO,
BindOnce(&VerifyTaskEnvironment, traits_,
use_resource_efficient_group_));
}
}
const TaskTraits traits_;
bool use_resource_efficient_group_;
test::TestTaskFactory factory_;
};
std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairs() {
std::vector<TraitsExecutionModePair> params;
constexpr TaskSourceExecutionMode execution_modes[] = {
TaskSourceExecutionMode::kParallel, TaskSourceExecutionMode::kSequenced,
TaskSourceExecutionMode::kSingleThread};
constexpr ThreadPolicy thread_policies[] = {
ThreadPolicy::PREFER_BACKGROUND, ThreadPolicy::MUST_USE_FOREGROUND};
for (TaskSourceExecutionMode execution_mode : execution_modes) {
for (ThreadPolicy thread_policy : thread_policies) {
for (size_t priority_index = static_cast<size_t>(TaskPriority::LOWEST);
priority_index <= static_cast<size_t>(TaskPriority::HIGHEST);
++priority_index) {
const TaskPriority priority = static_cast<TaskPriority>(priority_index);
params.push_back(
TraitsExecutionModePair({priority, thread_policy}, execution_mode));
params.push_back(TraitsExecutionModePair(
{priority, thread_policy, MayBlock()}, execution_mode));
}
}
}
return params;
}
std::vector<TraitsExecutionModePair>
GetTraitsExecutionModePairsToCoverAllSchedulingOptions() {
return {TraitsExecutionModePair({TaskPriority::BEST_EFFORT},
TaskSourceExecutionMode::kSequenced),
TraitsExecutionModePair({TaskPriority::USER_VISIBLE},
TaskSourceExecutionMode::kSequenced),
TraitsExecutionModePair({TaskPriority::USER_BLOCKING},
TaskSourceExecutionMode::kSequenced),
TraitsExecutionModePair({TaskPriority::BEST_EFFORT},
TaskSourceExecutionMode::kSingleThread),
TraitsExecutionModePair({TaskPriority::USER_VISIBLE},
TaskSourceExecutionMode::kSingleThread),
TraitsExecutionModePair({TaskPriority::USER_BLOCKING},
TaskSourceExecutionMode::kSingleThread)};
}
class ThreadPoolImplTestBase : public testing::Test {
public:
ThreadPoolImplTestBase()
: thread_pool_(std::make_unique<ThreadPoolImpl>("Test")),
service_thread_("ServiceThread") {
Thread::Options service_thread_options;
service_thread_options.message_pump_type = MessagePumpType::IO;
service_thread_.StartWithOptions(std::move(service_thread_options));
}
ThreadPoolImplTestBase(const ThreadPoolImplTestBase&) = delete;
ThreadPoolImplTestBase& operator=(const ThreadPoolImplTestBase&) = delete;
virtual bool GetUseResourceEfficientThreadGroup() const = 0;
void set_worker_thread_observer(
std::unique_ptr<WorkerThreadObserver> worker_thread_observer) {
worker_thread_observer_ = std::move(worker_thread_observer);
}
void StartThreadPool(
size_t max_num_foreground_threads = kMaxNumForegroundThreads,
size_t max_num_utility_threads = kMaxNumUtilityThreads,
TimeDelta reclaim_time = Seconds(30)) {
SetupFeatures();
ThreadPoolInstance::InitParams init_params(max_num_foreground_threads,
max_num_utility_threads);
init_params.suggested_reclaim_time = reclaim_time;
thread_pool_->Start(init_params, worker_thread_observer_.get());
}
void TearDown() override {
if (did_tear_down_) {
return;
}
if (thread_pool_) {
thread_pool_->FlushForTesting();
thread_pool_->JoinForTesting();
thread_pool_.reset();
}
did_tear_down_ = true;
}
std::unique_ptr<ThreadPoolImpl> thread_pool_;
Thread service_thread_;
private:
void SetupFeatures() {
std::vector<base::test::FeatureRef> features;
if (GetUseResourceEfficientThreadGroup()) {
features.push_back(kUseUtilityThreadGroup);
}
if (!features.empty()) {
feature_list_.InitWithFeatures(features, {});
}
}
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<WorkerThreadObserver> worker_thread_observer_;
bool did_tear_down_ = false;
};
class ThreadPoolImplTest : public ThreadPoolImplTestBase,
public testing::WithParamInterface<
bool > {
public:
bool GetUseResourceEfficientThreadGroup() const override {
return GetParam();
}
};
class ThreadPoolImplTest_CoverAllSchedulingOptions
: public ThreadPoolImplTestBase,
public testing::WithParamInterface<
std::tuple<bool /* use_resource_efficient_thread_group */,
TraitsExecutionModePair>> {
public:
ThreadPoolImplTest_CoverAllSchedulingOptions() = default;
ThreadPoolImplTest_CoverAllSchedulingOptions(
const ThreadPoolImplTest_CoverAllSchedulingOptions&) = delete;
ThreadPoolImplTest_CoverAllSchedulingOptions& operator=(
const ThreadPoolImplTest_CoverAllSchedulingOptions&) = delete;
bool GetUseResourceEfficientThreadGroup() const override {
return std::get<0>(GetParam());
}
TaskTraits GetTraits() const { return std::get<1>(GetParam()).traits; }
TaskSourceExecutionMode GetExecutionMode() const {
return std::get<1>(GetParam()).execution_mode;
}
};
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskNoDelay) {
StartThreadPool();
TestWaitableEvent task_ran;
thread_pool_->PostDelayedTask(
FROM_HERE, GetTraits(),
BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(),
GetUseResourceEfficientThreadGroup(), Unretained(&task_ran)),
TimeDelta());
task_ran.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskWithDelay) {
StartThreadPool();
TestWaitableEvent task_ran;
thread_pool_->PostDelayedTask(
FROM_HERE, GetTraits(),
BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(),
GetUseResourceEfficientThreadGroup(),
TimeTicks::Now() + TestTimeouts::tiny_timeout(),
Unretained(&task_ran)),
TestTimeouts::tiny_timeout());
task_ran.Wait();
}
namespace {
scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerAndExecutionMode(
ThreadPoolImpl* thread_pool,
const TaskTraits& traits,
TaskSourceExecutionMode execution_mode,
SingleThreadTaskRunnerThreadMode default_single_thread_task_runner_mode =
SingleThreadTaskRunnerThreadMode::SHARED) {
switch (execution_mode) {
case TaskSourceExecutionMode::kSequenced:
return thread_pool->CreateSequencedTaskRunner(traits);
case TaskSourceExecutionMode::kSingleThread: {
return thread_pool->CreateSingleThreadTaskRunner(
traits, default_single_thread_task_runner_mode);
}
case TaskSourceExecutionMode::kParallel:
case TaskSourceExecutionMode::kJob:
ADD_FAILURE() << "Tests below don't cover these modes";
return nullptr;
}
ADD_FAILURE() << "Unknown ExecutionMode";
return nullptr;
}
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostDelayedTaskAtViaTaskRunner) {
StartThreadPool();
TestWaitableEvent task_ran;
auto handle =
CreateSequencedTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostCancelableDelayedTaskAt(
subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(),
GetUseResourceEfficientThreadGroup(),
TimeTicks::Now() + TestTimeouts::tiny_timeout(),
Unretained(&task_ran)),
TimeTicks::Now() + TestTimeouts::tiny_timeout(),
subtle::DelayPolicy::kFlexibleNoSooner);
task_ran.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTasksViaTaskRunner) {
StartThreadPool();
test::TestTaskFactory factory(
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode()),
GetExecutionMode());
const size_t kNumTasksPerTest = 150;
for (size_t i = 0; i < kNumTasksPerTest; ++i) {
factory.PostTask(test::TestTaskFactory::PostNestedTask::NO,
BindOnce(&VerifyTaskEnvironment, GetTraits(),
GetUseResourceEfficientThreadGroup()));
}
factory.WaitForAllTasksToRun();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostDelayedTaskNoDelayBeforeStart) {
TestWaitableEvent task_running;
thread_pool_->PostDelayedTask(
FROM_HERE, GetTraits(),
BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(),
GetUseResourceEfficientThreadGroup(), Unretained(&task_running)),
TimeDelta());
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(task_running.IsSignaled());
StartThreadPool();
task_running.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostDelayedTaskWithDelayBeforeStart) {
TestWaitableEvent task_running;
thread_pool_->PostDelayedTask(
FROM_HERE, GetTraits(),
BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(),
GetUseResourceEfficientThreadGroup(),
TimeTicks::Now() + TestTimeouts::tiny_timeout(),
Unretained(&task_running)),
TestTimeouts::tiny_timeout());
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(task_running.IsSignaled());
StartThreadPool();
task_running.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostTaskViaTaskRunnerBeforeStart) {
bool use_resource_efficient_thread_group =
GetUseResourceEfficientThreadGroup();
if (GetExecutionMode() == TaskSourceExecutionMode::kSingleThread) {
use_resource_efficient_thread_group = false;
}
TestWaitableEvent task_running;
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE,
BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(),
use_resource_efficient_thread_group,
Unretained(&task_running)));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(task_running.IsSignaled());
StartThreadPool();
task_running.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTaskAfterDestroy) {
StartThreadPool();
auto task_runner = CreateTaskRunnerAndExecutionMode(
thread_pool_.get(), GetTraits(), GetExecutionMode());
EXPECT_TRUE(task_runner->PostTask(FROM_HERE, DoNothing()));
thread_pool_->JoinForTesting();
thread_pool_.reset();
EXPECT_FALSE(
task_runner->PostTask(FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
FlushAsyncForTestingSimple) {
StartThreadPool();
TestWaitableEvent unblock_task;
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode(),
SingleThreadTaskRunnerThreadMode::DEDICATED)
->PostTask(FROM_HERE,
BindOnce(&TestWaitableEvent::Wait, Unretained(&unblock_task)));
TestWaitableEvent flush_event;
thread_pool_->FlushAsyncForTesting(
BindOnce(&TestWaitableEvent::Signal, Unretained(&flush_event)));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(flush_event.IsSignaled());
unblock_task.Signal();
flush_event.Wait();
}
TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableBestEffortTasks);
ThreadPoolImpl thread_pool("Test");
ThreadPoolInstance::InitParams init_params(kMaxNumForegroundThreads,
kMaxNumUtilityThreads);
thread_pool.Start(init_params, nullptr);
AtomicFlag best_effort_can_run;
TestWaitableEvent best_effort_did_run;
thread_pool.PostDelayedTask(
FROM_HERE,
{TaskPriority::BEST_EFFORT, TaskShutdownBehavior::BLOCK_SHUTDOWN},
BindLambdaForTesting([&] {
EXPECT_TRUE(best_effort_can_run.IsSet());
best_effort_did_run.Signal();
}),
TimeDelta());
TestWaitableEvent user_blocking_did_run;
thread_pool.PostDelayedTask(
FROM_HERE, {TaskPriority::USER_BLOCKING},
BindLambdaForTesting([&] { user_blocking_did_run.Signal(); }),
TimeDelta());
user_blocking_did_run.Wait();
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
thread_pool.BeginBestEffortFence();
thread_pool.EndBestEffortFence();
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
best_effort_can_run.Set();
thread_pool.Shutdown();
EXPECT_TRUE(best_effort_did_run.IsSignaled());
thread_pool.JoinForTesting();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, Fence) {
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
thread_pool_->BeginFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
EXPECT_TRUE(can_run.IsSet());
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleFences) {
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
thread_pool_->BeginFence();
thread_pool_->BeginFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
EXPECT_TRUE(can_run.IsSet());
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
thread_pool_->EndFence();
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, FenceBeforeStart) {
thread_pool_->BeginFence();
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
EXPECT_TRUE(can_run.IsSet());
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, BestEffortFence) {
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
thread_pool_->BeginBestEffortFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
if (GetTraits().priority() == TaskPriority::BEST_EFFORT) {
EXPECT_TRUE(can_run.IsSet());
}
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndBestEffortFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleBestEffortFences) {
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
thread_pool_->BeginBestEffortFence();
thread_pool_->BeginBestEffortFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
if (GetTraits().priority() == TaskPriority::BEST_EFFORT) {
EXPECT_TRUE(can_run.IsSet());
}
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
thread_pool_->EndBestEffortFence();
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndBestEffortFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
BestEffortFenceBeforeStart) {
thread_pool_->BeginBestEffortFence();
StartThreadPool();
AtomicFlag can_run;
TestWaitableEvent did_run;
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&] {
if (GetTraits().priority() == TaskPriority::BEST_EFFORT) {
EXPECT_TRUE(can_run.IsSet());
}
did_run.Signal();
}));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set();
thread_pool_->EndBestEffortFence();
did_run.Wait();
}
TEST_P(ThreadPoolImplTest, MultipleTraitsExecutionModePair) {
StartThreadPool();
std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
for (const auto& test_params : GetTraitsExecutionModePairs()) {
threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>(
thread_pool_.get(), test_params.traits,
GetUseResourceEfficientThreadGroup(), test_params.execution_mode));
threads_posting_tasks.back()->Start();
}
for (const auto& thread : threads_posting_tasks) {
thread->WaitForAllTasksToRun();
thread->Join();
}
}
TEST_P(ThreadPoolImplTest,
GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) {
StartThreadPool();
GTEST_FLAG_SET(death_test_style, "threadsafe");
EXPECT_DCHECK_DEATH({
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{TaskPriority::BEST_EFFORT});
});
EXPECT_DCHECK_DEATH({
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{MayBlock(), TaskPriority::BEST_EFFORT});
});
EXPECT_EQ(GetUseResourceEfficientThreadGroup() &&
CanUseUtilityThreadTypeForWorkerThread()
? kMaxNumUtilityThreads
: kMaxNumForegroundThreads,
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{TaskPriority::USER_VISIBLE}));
EXPECT_EQ(GetUseResourceEfficientThreadGroup() &&
CanUseUtilityThreadTypeForWorkerThread()
? kMaxNumUtilityThreads
: kMaxNumForegroundThreads,
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{MayBlock(), TaskPriority::USER_VISIBLE}));
EXPECT_EQ(kMaxNumForegroundThreads,
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{TaskPriority::USER_BLOCKING}));
EXPECT_EQ(kMaxNumForegroundThreads,
thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
{MayBlock(), TaskPriority::USER_BLOCKING}));
}
TEST_P(ThreadPoolImplTest, SequencedRunsTasksInCurrentSequence) {
StartThreadPool();
auto single_thread_task_runner = thread_pool_->CreateSingleThreadTaskRunner(
{}, SingleThreadTaskRunnerThreadMode::SHARED);
auto sequenced_task_runner = thread_pool_->CreateSequencedTaskRunner({});
TestWaitableEvent task_ran;
single_thread_task_runner->PostTask(
FROM_HERE,
BindOnce(
[](scoped_refptr<SequencedTaskRunner> sequenced_task_runner,
TestWaitableEvent* task_ran) {
EXPECT_FALSE(sequenced_task_runner->RunsTasksInCurrentSequence());
task_ran->Signal();
},
sequenced_task_runner, Unretained(&task_ran)));
task_ran.Wait();
}
TEST_P(ThreadPoolImplTest, SingleThreadRunsTasksInCurrentSequence) {
StartThreadPool();
auto sequenced_task_runner = thread_pool_->CreateSequencedTaskRunner({});
auto single_thread_task_runner = thread_pool_->CreateSingleThreadTaskRunner(
{}, SingleThreadTaskRunnerThreadMode::SHARED);
TestWaitableEvent task_ran;
sequenced_task_runner->PostTask(
FROM_HERE,
BindOnce(
[](scoped_refptr<SingleThreadTaskRunner> single_thread_task_runner,
TestWaitableEvent* task_ran) {
EXPECT_FALSE(
single_thread_task_runner->RunsTasksInCurrentSequence());
task_ran->Signal();
},
single_thread_task_runner, Unretained(&task_ran)));
task_ran.Wait();
}
#if BUILDFLAG(IS_WIN)
TEST_P(ThreadPoolImplTest, COMSTATaskRunnersRunWithCOMSTA) {
StartThreadPool();
auto com_sta_task_runner = thread_pool_->CreateCOMSTATaskRunner(
{}, SingleThreadTaskRunnerThreadMode::SHARED);
TestWaitableEvent task_ran;
com_sta_task_runner->PostTask(
FROM_HERE, BindOnce(
[](TestWaitableEvent* task_ran) {
win::AssertComApartmentType(win::ComApartmentType::STA);
task_ran->Signal();
},
Unretained(&task_ran)));
task_ran.Wait();
}
#endif
TEST_P(ThreadPoolImplTest, DelayedTasksNotRunAfterShutdown) {
StartThreadPool();
thread_pool_->PostDelayedTask(FROM_HERE, {}, BindOnce([] { ADD_FAILURE(); }),
TestTimeouts::tiny_timeout());
thread_pool_->Shutdown();
PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 2);
}
#if BUILDFLAG(IS_POSIX)
TEST_P(ThreadPoolImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {
StartThreadPool();
int pipes[2];
ASSERT_EQ(0, pipe(pipes));
scoped_refptr<TaskRunner> blocking_task_runner =
thread_pool_->CreateSequencedTaskRunner(
{TaskShutdownBehavior::BLOCK_SHUTDOWN});
blocking_task_runner->PostTask(
FROM_HERE,
BindOnce(
[](int read_fd) {
std::unique_ptr<FileDescriptorWatcher::Controller> controller =
FileDescriptorWatcher::WatchReadable(
read_fd, BindRepeating([] { NOTREACHED(); }));
ANNOTATE_LEAKING_OBJECT_PTR(controller.get());
controller.release();
},
pipes[0]));
thread_pool_->Shutdown();
constexpr char kByte = '!';
ASSERT_TRUE(WriteFileDescriptor(pipes[1], byte_span_from_ref(kByte)));
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_EQ(0, IGNORE_EINTR(close(pipes[0])));
EXPECT_EQ(0, IGNORE_EINTR(close(pipes[1])));
}
#endif
#if BUILDFLAG(IS_POSIX)
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, FileDescriptorWatcher) {
StartThreadPool();
int fds[2];
ASSERT_EQ(0, pipe(fds));
auto task_runner = CreateTaskRunnerAndExecutionMode(
thread_pool_.get(), GetTraits(), GetExecutionMode());
EXPECT_TRUE(task_runner->PostTask(
FROM_HERE, BindOnce(IgnoreResult(&FileDescriptorWatcher::WatchReadable),
fds[0], DoNothing())));
thread_pool_->FlushForTesting();
EXPECT_EQ(0, IGNORE_EINTR(close(fds[0])));
EXPECT_EQ(0, IGNORE_EINTR(close(fds[1])));
}
#endif
TEST_P(ThreadPoolImplTest, SequenceLocalStorage) {
StartThreadPool();
SequenceLocalStorageSlot<int> slot;
auto sequenced_task_runner1 = thread_pool_->CreateSequencedTaskRunner({});
auto sequenced_task_runner2 = thread_pool_->CreateSequencedTaskRunner({});
sequenced_task_runner1->PostTask(
FROM_HERE,
BindOnce([](SequenceLocalStorageSlot<int>* slot) { slot->emplace(11); },
&slot));
sequenced_task_runner1->PostTask(
FROM_HERE, BindOnce(
[](SequenceLocalStorageSlot<int>* slot) {
EXPECT_EQ(slot->GetOrCreateValue(), 11);
},
&slot));
sequenced_task_runner2->PostTask(
FROM_HERE, BindOnce(
[](SequenceLocalStorageSlot<int>* slot) {
EXPECT_NE(slot->GetOrCreateValue(), 11);
},
&slot));
thread_pool_->FlushForTesting();
}
TEST_P(ThreadPoolImplTest, FlushAsyncNoTasks) {
StartThreadPool();
bool called_back = false;
thread_pool_->FlushAsyncForTesting(
BindOnce([](bool* called_back) { *called_back = true; },
Unretained(&called_back)));
EXPECT_TRUE(called_back);
}
TEST_P(ThreadPoolImplTest, CreateSequencedTaskRunnerForResource) {
StartThreadPool();
scoped_refptr<SequencedTaskRunner> task_runner1 =
thread_pool_->CreateSequencedTaskRunnerForResource(
{}, base::FilePath(FILE_PATH_LITERAL("Resource1")));
scoped_refptr<SequencedTaskRunner> task_runner2 =
thread_pool_->CreateSequencedTaskRunnerForResource(
{}, base::FilePath(FILE_PATH_LITERAL("Resource2")));
scoped_refptr<SequencedTaskRunner> task_runner3 =
thread_pool_->CreateSequencedTaskRunnerForResource(
{}, base::FilePath(FILE_PATH_LITERAL("Resource1")));
EXPECT_NE(task_runner1, task_runner2);
EXPECT_EQ(task_runner1, task_runner3);
}
namespace {
void VerifyHasStringsOnStack(const std::string& pool_str,
const std::string& shutdown_behavior_str) {
const std::string stack = debug::StackTrace().ToString();
SCOPED_TRACE(stack);
const bool stack_has_symbols =
stack.find("WorkerThread") != std::string::npos;
if (!stack_has_symbols) {
return;
}
EXPECT_THAT(stack, ::testing::HasSubstr(pool_str));
EXPECT_THAT(stack, ::testing::HasSubstr(shutdown_behavior_str));
}
}
#if BUILDFLAG(IS_POSIX)
#define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
#elif BUILDFLAG(IS_WIN) && \
(defined(ADDRESS_SANITIZER) || BUILDFLAG(CFI_CAST_CHECK))
#define MAYBE_IdentifiableStacks DISABLED_IdentifiableStacks
#else
#define MAYBE_IdentifiableStacks IdentifiableStacks
#endif
TEST_P(ThreadPoolImplTest, MAYBE_IdentifiableStacks) {
StartThreadPool();
constexpr std::pair<TaskShutdownBehavior, const char*> shutdown_behaviors[] =
{{TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, "RunContinueOnShutdown"},
{TaskShutdownBehavior::SKIP_ON_SHUTDOWN, "RunSkipOnShutdown"},
{TaskShutdownBehavior::BLOCK_SHUTDOWN, "RunBlockShutdown"}};
for (const auto& shutdown_behavior : shutdown_behaviors) {
const TaskTraits traits = {shutdown_behavior.first};
const TaskTraits best_effort_traits = {shutdown_behavior.first,
TaskPriority::BEST_EFFORT};
thread_pool_->CreateSequencedTaskRunner(traits)->PostTask(
FROM_HERE, BindOnce(&VerifyHasStringsOnStack, "RunPooledWorker",
shutdown_behavior.second));
thread_pool_->CreateSequencedTaskRunner(best_effort_traits)
->PostTask(FROM_HERE, BindOnce(&VerifyHasStringsOnStack,
"RunBackgroundPooledWorker",
shutdown_behavior.second));
thread_pool_
->CreateSingleThreadTaskRunner(traits,
SingleThreadTaskRunnerThreadMode::SHARED)
->PostTask(FROM_HERE,
BindOnce(&VerifyHasStringsOnStack, "RunSharedWorker",
shutdown_behavior.second));
thread_pool_
->CreateSingleThreadTaskRunner(best_effort_traits,
SingleThreadTaskRunnerThreadMode::SHARED)
->PostTask(FROM_HERE, BindOnce(&VerifyHasStringsOnStack,
"RunBackgroundSharedWorker",
shutdown_behavior.second));
thread_pool_
->CreateSingleThreadTaskRunner(
traits, SingleThreadTaskRunnerThreadMode::DEDICATED)
->PostTask(FROM_HERE,
BindOnce(&VerifyHasStringsOnStack, "RunDedicatedWorker",
shutdown_behavior.second));
thread_pool_
->CreateSingleThreadTaskRunner(
best_effort_traits, SingleThreadTaskRunnerThreadMode::DEDICATED)
->PostTask(FROM_HERE, BindOnce(&VerifyHasStringsOnStack,
"RunBackgroundDedicatedWorker",
shutdown_behavior.second));
#if BUILDFLAG(IS_WIN)
thread_pool_
->CreateCOMSTATaskRunner(traits,
SingleThreadTaskRunnerThreadMode::SHARED)
->PostTask(FROM_HERE,
BindOnce(&VerifyHasStringsOnStack, "RunSharedCOMWorker",
shutdown_behavior.second));
thread_pool_
->CreateCOMSTATaskRunner(best_effort_traits,
SingleThreadTaskRunnerThreadMode::SHARED)
->PostTask(FROM_HERE, BindOnce(&VerifyHasStringsOnStack,
"RunBackgroundSharedCOMWorker",
shutdown_behavior.second));
thread_pool_
->CreateCOMSTATaskRunner(traits,
SingleThreadTaskRunnerThreadMode::DEDICATED)
->PostTask(FROM_HERE,
BindOnce(&VerifyHasStringsOnStack, "RunDedicatedCOMWorker",
shutdown_behavior.second));
thread_pool_
->CreateCOMSTATaskRunner(best_effort_traits,
SingleThreadTaskRunnerThreadMode::DEDICATED)
->PostTask(FROM_HERE, BindOnce(&VerifyHasStringsOnStack,
"RunBackgroundDedicatedCOMWorker",
shutdown_behavior.second));
#endif
}
thread_pool_->FlushForTesting();
}
TEST_P(ThreadPoolImplTest, WorkerThreadObserver) {
auto owned_observer =
std::make_unique<testing::StrictMock<test::MockWorkerThreadObserver>>();
auto* observer = owned_observer.get();
set_worker_thread_observer(std::move(owned_observer));
const int kExpectedNumForegroundPoolWorkers = 1;
const int kExpectedNumUtilityPoolWorkers =
GetUseResourceEfficientThreadGroup() &&
CanUseUtilityThreadTypeForWorkerThread()
? 1
: 0;
const int kExpectedNumBackgroundPoolWorkers =
CanUseBackgroundThreadTypeForWorkerThread() ? 1 : 0;
const int kExpectedNumPoolWorkers = kExpectedNumForegroundPoolWorkers +
kExpectedNumUtilityPoolWorkers +
kExpectedNumBackgroundPoolWorkers;
const int kExpectedNumSharedSingleThreadedForegroundWorkers = 2;
const int kExpectedNumSharedSingleThreadedUtilityWorkers =
GetUseResourceEfficientThreadGroup() &&
CanUseUtilityThreadTypeForWorkerThread()
? 2
: 0;
const int kExpectedNumSharedSingleThreadedBackgroundWorkers =
CanUseBackgroundThreadTypeForWorkerThread() ? 2 : 0;
const int kExpectedNumSharedSingleThreadedWorkers =
kExpectedNumSharedSingleThreadedForegroundWorkers +
kExpectedNumSharedSingleThreadedUtilityWorkers +
kExpectedNumSharedSingleThreadedBackgroundWorkers;
const int kExpectedNumDedicatedSingleThreadedWorkers = 6;
const int kExpectedNumCOMSharedSingleThreadedWorkers =
#if BUILDFLAG(IS_WIN)
kExpectedNumSharedSingleThreadedWorkers;
#else
0;
#endif
const int kExpectedNumCOMDedicatedSingleThreadedWorkers =
#if BUILDFLAG(IS_WIN)
kExpectedNumDedicatedSingleThreadedWorkers;
#else
0;
#endif
EXPECT_CALL(*observer, OnWorkerThreadMainEntry())
.Times(kExpectedNumPoolWorkers + kExpectedNumSharedSingleThreadedWorkers +
kExpectedNumDedicatedSingleThreadedWorkers +
kExpectedNumCOMSharedSingleThreadedWorkers +
kExpectedNumCOMDedicatedSingleThreadedWorkers);
StartThreadPool(kMaxNumForegroundThreads, kMaxNumUtilityThreads,
TimeDelta::Max());
std::vector<scoped_refptr<SingleThreadTaskRunner>> task_runners;
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::BEST_EFFORT}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::BEST_EFFORT, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_VISIBLE, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_BLOCKING, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::BEST_EFFORT},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::BEST_EFFORT, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_VISIBLE},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_VISIBLE, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_BLOCKING},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner(
{TaskPriority::USER_BLOCKING, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
#if BUILDFLAG(IS_WIN)
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::BEST_EFFORT}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::BEST_EFFORT, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_VISIBLE, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_BLOCKING, MayBlock()},
SingleThreadTaskRunnerThreadMode::SHARED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::BEST_EFFORT},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::BEST_EFFORT, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_VISIBLE},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_VISIBLE, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_BLOCKING},
SingleThreadTaskRunnerThreadMode::DEDICATED));
task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner(
{TaskPriority::USER_BLOCKING, MayBlock()},
SingleThreadTaskRunnerThreadMode::DEDICATED));
#endif
for (auto& task_runner : task_runners) {
task_runner->PostTask(FROM_HERE, DoNothing());
}
observer->AllowCallsOnMainExit(kExpectedNumDedicatedSingleThreadedWorkers +
kExpectedNumCOMDedicatedSingleThreadedWorkers);
task_runners.clear();
observer->WaitCallsOnMainExit();
observer->AllowCallsOnMainExit(kExpectedNumPoolWorkers +
kExpectedNumSharedSingleThreadedWorkers +
kExpectedNumCOMSharedSingleThreadedWorkers);
TearDown();
observer->WaitCallsOnMainExit();
}
TEST_P(ThreadPoolImplTest, ScheduleJobTaskSource) {
StartThreadPool();
TestWaitableEvent threads_running;
auto job_task = base::MakeRefCounted<test::MockJobTask>(
BindLambdaForTesting(
[&threads_running](JobDelegate*) { threads_running.Signal(); }),
1);
scoped_refptr<JobTaskSource> task_source =
job_task->GetJobTaskSource(FROM_HERE, {}, thread_pool_.get());
thread_pool_->EnqueueJobTaskSource(task_source);
threads_running.Wait();
}
TEST_P(ThreadPoolImplTest, ThreadGroupChangeShouldYield) {
StartThreadPool();
TestWaitableEvent threads_running;
TestWaitableEvent threads_continue;
auto job_task = base::MakeRefCounted<test::MockJobTask>(
BindLambdaForTesting(
[&threads_running, &threads_continue](JobDelegate* delegate) {
EXPECT_FALSE(delegate->ShouldYield());
threads_running.Signal();
threads_continue.Wait();
EXPECT_EQ(delegate->ShouldYield(),
CanUseBackgroundThreadTypeForWorkerThread());
}),
1);
scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
FROM_HERE, {TaskPriority::USER_VISIBLE}, thread_pool_.get());
thread_pool_->EnqueueJobTaskSource(task_source);
threads_running.Wait();
thread_pool_->UpdatePriority(task_source, TaskPriority::BEST_EFFORT);
threads_continue.Signal();
thread_pool_->FlushForTesting();
}
namespace {
class MustBeDestroyed {
public:
explicit MustBeDestroyed(bool* was_destroyed)
: was_destroyed_(was_destroyed) {}
MustBeDestroyed(const MustBeDestroyed&) = delete;
MustBeDestroyed& operator=(const MustBeDestroyed&) = delete;
~MustBeDestroyed() { *was_destroyed_ = true; }
private:
const raw_ptr<bool> was_destroyed_;
};
}
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
NoLeakWhenPostingNestedTask) {
StartThreadPool();
SequenceLocalStorageSlot<std::unique_ptr<MustBeDestroyed>> sls;
bool was_destroyed = false;
auto must_be_destroyed = std::make_unique<MustBeDestroyed>(&was_destroyed);
auto task_runner = CreateTaskRunnerAndExecutionMode(
thread_pool_.get(), GetTraits(), GetExecutionMode());
task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&] {
sls.emplace(std::move(must_be_destroyed));
task_runner->PostTask(FROM_HERE, DoNothing());
}));
TearDown();
task_runner = nullptr;
EXPECT_TRUE(was_destroyed);
}
namespace {
struct TaskRunnerAndEvents {
TaskRunnerAndEvents(scoped_refptr<UpdateableSequencedTaskRunner> task_runner,
const TaskPriority updated_priority,
TestWaitableEvent* expected_previous_event)
: task_runner(std::move(task_runner)),
updated_priority(updated_priority),
expected_previous_event(expected_previous_event) {}
scoped_refptr<UpdateableSequencedTaskRunner> task_runner;
const TaskPriority updated_priority;
TestWaitableEvent scheduled;
TestWaitableEvent blocked;
TestWaitableEvent task_ran;
raw_ptr<TestWaitableEvent> expected_previous_event;
};
std::vector<std::unique_ptr<TaskRunnerAndEvents>> CreateTaskRunnersAndEvents(
ThreadPoolImplTest* test,
ThreadPolicy thread_policy) {
ThreadPoolImpl* thread_pool = test->thread_pool_.get();
std::vector<std::unique_ptr<TaskRunnerAndEvents>> task_runners_and_events;
task_runners_and_events.push_back(std::make_unique<TaskRunnerAndEvents>(
thread_pool->CreateUpdateableSequencedTaskRunner(
TaskTraits({TaskPriority::USER_VISIBLE, thread_policy})),
TaskPriority::USER_BLOCKING, nullptr));
TestWaitableEvent* expected_previous_event =
test->GetUseResourceEfficientThreadGroup()
? nullptr
: &task_runners_and_events.back()->task_ran;
task_runners_and_events.push_back(std::make_unique<TaskRunnerAndEvents>(
thread_pool->CreateUpdateableSequencedTaskRunner(
{TaskPriority::BEST_EFFORT, thread_policy}),
TaskPriority::USER_VISIBLE, expected_previous_event));
expected_previous_event =
CanUseBackgroundThreadTypeForWorkerThread() ||
(test->GetUseResourceEfficientThreadGroup() &&
CanUseUtilityThreadTypeForWorkerThread())
? nullptr
: &task_runners_and_events.back()->task_ran;
task_runners_and_events.push_back(std::make_unique<TaskRunnerAndEvents>(
thread_pool->CreateUpdateableSequencedTaskRunner(
TaskTraits({TaskPriority::USER_BLOCKING, thread_policy})),
TaskPriority::BEST_EFFORT, expected_previous_event));
return task_runners_and_events;
}
void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test,
ThreadPolicy thread_policy) {
constexpr size_t kLocalMaxNumForegroundThreads = 1;
test->StartThreadPool(kLocalMaxNumForegroundThreads);
auto task_runners_and_events =
CreateTaskRunnersAndEvents(test, thread_policy);
test->thread_pool_->BeginFence();
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->PostTask(
FROM_HERE,
BindOnce(
&VerifyOrderAndTaskEnvironmentAndSignalEvent,
TaskTraits{task_runner_and_events->updated_priority, thread_policy},
test->GetUseResourceEfficientThreadGroup(),
Unretained(task_runner_and_events->expected_previous_event.get()),
Unretained(&task_runner_and_events->task_ran)));
}
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->UpdatePriority(
task_runner_and_events->updated_priority);
}
test->thread_pool_->EndFence();
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_ran.Wait();
}
}
void TestUpdatePrioritySequenceScheduled(ThreadPoolImplTest* test,
ThreadPolicy thread_policy) {
test->StartThreadPool();
auto task_runners_and_events =
CreateTaskRunnersAndEvents(test, thread_policy);
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->PostTask(
FROM_HERE, BindLambdaForTesting([&] {
task_runner_and_events->scheduled.Signal();
task_runner_and_events->blocked.Wait();
}));
task_runner_and_events->scheduled.Wait();
}
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->UpdatePriority(
task_runner_and_events->updated_priority);
}
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->PostTask(
FROM_HERE,
BindOnce(
&VerifyOrderAndTaskEnvironmentAndSignalEvent,
TaskTraits{task_runner_and_events->updated_priority, thread_policy},
test->GetUseResourceEfficientThreadGroup(),
Unretained(task_runner_and_events->expected_previous_event),
Unretained(&task_runner_and_events->task_ran)));
}
for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->blocked.Signal();
task_runner_and_events->task_ran.Wait();
}
}
}
TEST_P(ThreadPoolImplTest,
UpdatePrioritySequenceNotScheduled_PreferBackground) {
TestUpdatePrioritySequenceNotScheduled(this, ThreadPolicy::PREFER_BACKGROUND);
}
TEST_P(ThreadPoolImplTest,
UpdatePrioritySequenceNotScheduled_MustUseForeground) {
TestUpdatePrioritySequenceNotScheduled(this,
ThreadPolicy::MUST_USE_FOREGROUND);
}
TEST_P(ThreadPoolImplTest, UpdatePrioritySequenceScheduled_PreferBackground) {
TestUpdatePrioritySequenceScheduled(this, ThreadPolicy::PREFER_BACKGROUND);
}
TEST_P(ThreadPoolImplTest, UpdatePrioritySequenceScheduled_MustUseForeground) {
TestUpdatePrioritySequenceScheduled(this, ThreadPolicy::MUST_USE_FOREGROUND);
}
TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) {
GTEST_FLAG_SET(death_test_style, "threadsafe");
StartThreadPool();
{
auto task_runner = thread_pool_->CreateUpdateableSequencedTaskRunner(
{TaskPriority::BEST_EFFORT});
EXPECT_DCHECK_DEATH(
{ task_runner->UpdatePriority(TaskPriority::USER_VISIBLE); });
}
{
auto task_runner = thread_pool_->CreateUpdateableSequencedTaskRunner(
{TaskPriority::BEST_EFFORT});
EXPECT_DCHECK_DEATH(
{ task_runner->UpdatePriority(TaskPriority::USER_BLOCKING); });
}
}
TEST_P(ThreadPoolImplTest, BestEffortFenceLiftedForFlush) {
StartThreadPool();
TestWaitableEvent best_effort_task_ran;
thread_pool_->BeginBestEffortFence();
thread_pool_->PostDelayedTask(
FROM_HERE, {TaskPriority::BEST_EFFORT},
BindOnce(&TestWaitableEvent::Signal, Unretained(&best_effort_task_ran)),
base::TimeDelta());
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(best_effort_task_ran.IsSignaled());
thread_pool_->FlushForTesting();
EXPECT_TRUE(best_effort_task_ran.IsSignaled());
}
TEST_P(ThreadPoolImplTest, FenceLiftedForFlush) {
StartThreadPool();
TestWaitableEvent task_ran;
thread_pool_->BeginFence();
thread_pool_->PostDelayedTask(
FROM_HERE, {TaskPriority::USER_VISIBLE},
BindOnce(&TestWaitableEvent::Signal, Unretained(&task_ran)),
base::TimeDelta());
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
EXPECT_FALSE(task_ran.IsSignaled());
thread_pool_->FlushForTesting();
EXPECT_TRUE(task_ran.IsSignaled());
}
INSTANTIATE_TEST_SUITE_P(All, ThreadPoolImplTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(
All,
ThreadPoolImplTest_CoverAllSchedulingOptions,
::testing::Combine(
::testing::Bool(),
::testing::ValuesIn(
GetTraitsExecutionModePairsToCoverAllSchedulingOptions())));
}