// Copyright 2017 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/lazy_task_runner.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/sequence_checker_impl.h" #include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h" #include "base/test/scoped_task_environment.h" #include "base/threading/thread_checker_impl.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) #include "base/win/com_init_util.h" #endif namespace base { namespace { LazySequencedTaskRunner g_sequenced_task_runner_user_visible = LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({TaskPriority::USER_VISIBLE}); LazySequencedTaskRunner g_sequenced_task_runner_user_blocking = LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({TaskPriority::USER_BLOCKING}); LazySingleThreadTaskRunner g_single_thread_task_runner_user_visible = LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER( {TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED); LazySingleThreadTaskRunner g_single_thread_task_runner_user_blocking = LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED); #if defined(OS_WIN) LazyCOMSTATaskRunner g_com_sta_task_runner_user_visible = LAZY_COM_STA_TASK_RUNNER_INITIALIZER( {TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED); LazyCOMSTATaskRunner g_com_sta_task_runner_user_blocking = LAZY_COM_STA_TASK_RUNNER_INITIALIZER( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED); #endif // defined(OS_WIN) void InitCheckers(SequenceCheckerImpl* sequence_checker, ThreadCheckerImpl* thread_checker) { sequence_checker->DetachFromSequence(); EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); thread_checker->DetachFromThread(); EXPECT_TRUE(thread_checker->CalledOnValidThread()); } void ExpectSequencedEnvironment(SequenceCheckerImpl* sequence_checker, ThreadCheckerImpl* thread_checker, TaskPriority expected_priority) { EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); EXPECT_FALSE(thread_checker->CalledOnValidThread()); EXPECT_EQ(expected_priority, internal::GetTaskPriorityForCurrentThread()); } void ExpectSingleThreadEnvironment(SequenceCheckerImpl* sequence_checker, ThreadCheckerImpl* thread_checker, TaskPriority expected_priority #if defined(OS_WIN) , bool expect_com_sta = false #endif ) { EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); EXPECT_TRUE(thread_checker->CalledOnValidThread()); EXPECT_EQ(expected_priority, internal::GetTaskPriorityForCurrentThread()); #if defined(OS_WIN) if (expect_com_sta) win::AssertComApartmentType(win::ComApartmentType::STA); #endif } class TaskSchedulerLazyTaskRunnerEnvironmentTest : public testing::Test { protected: TaskSchedulerLazyTaskRunnerEnvironmentTest() = default; void TestTaskRunnerEnvironment(scoped_refptr task_runner, bool expect_single_thread, TaskPriority expected_priority #if defined(OS_WIN) , bool expect_com_sta = false #endif ) { SequenceCheckerImpl sequence_checker; ThreadCheckerImpl thread_checker; task_runner->PostTask(FROM_HERE, BindOnce(&InitCheckers, Unretained(&sequence_checker), Unretained(&thread_checker))); scoped_task_environment_.RunUntilIdle(); OnceClosure task = expect_single_thread ? BindOnce(&ExpectSingleThreadEnvironment, Unretained(&sequence_checker), Unretained(&thread_checker), expected_priority #if defined(OS_WIN) , expect_com_sta #endif ) : BindOnce(&ExpectSequencedEnvironment, Unretained(&sequence_checker), Unretained(&thread_checker), expected_priority); task_runner->PostTask(FROM_HERE, std::move(task)); scoped_task_environment_.RunUntilIdle(); } test::ScopedTaskEnvironment scoped_task_environment_; private: DISALLOW_COPY_AND_ASSIGN(TaskSchedulerLazyTaskRunnerEnvironmentTest); }; } // namespace TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazySequencedTaskRunnerUserVisible) { TestTaskRunnerEnvironment(g_sequenced_task_runner_user_visible.Get(), false, TaskPriority::USER_VISIBLE); } TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazySequencedTaskRunnerUserBlocking) { TestTaskRunnerEnvironment(g_sequenced_task_runner_user_blocking.Get(), false, TaskPriority::USER_BLOCKING); } TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazySingleThreadTaskRunnerUserVisible) { TestTaskRunnerEnvironment(g_single_thread_task_runner_user_visible.Get(), true, TaskPriority::USER_VISIBLE); } TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazySingleThreadTaskRunnerUserBlocking) { TestTaskRunnerEnvironment(g_single_thread_task_runner_user_blocking.Get(), true, TaskPriority::USER_BLOCKING); } #if defined(OS_WIN) TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazyCOMSTATaskRunnerUserVisible) { TestTaskRunnerEnvironment(g_com_sta_task_runner_user_visible.Get(), true, TaskPriority::USER_VISIBLE, true); } TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, LazyCOMSTATaskRunnerUserBlocking) { TestTaskRunnerEnvironment(g_com_sta_task_runner_user_blocking.Get(), true, TaskPriority::USER_BLOCKING, true); } #endif // defined(OS_WIN) TEST(TaskSchdulerLazyTaskRunnerTest, LazySequencedTaskRunnerReset) { for (int i = 0; i < 2; ++i) { test::ScopedTaskEnvironment scoped_task_environment; // If the TaskRunner isn't released when the test::ScopedTaskEnvironment // goes out of scope, the second invocation of the line below will access a // deleted TaskScheduler and crash. g_sequenced_task_runner_user_visible.Get()->PostTask(FROM_HERE, DoNothing()); } } TEST(TaskSchdulerLazyTaskRunnerTest, LazySingleThreadTaskRunnerReset) { for (int i = 0; i < 2; ++i) { test::ScopedTaskEnvironment scoped_task_environment; // If the TaskRunner isn't released when the test::ScopedTaskEnvironment // goes out of scope, the second invocation of the line below will access a // deleted TaskScheduler and crash. g_single_thread_task_runner_user_visible.Get()->PostTask(FROM_HERE, DoNothing()); } } #if defined(OS_WIN) TEST(TaskSchdulerLazyTaskRunnerTest, LazyCOMSTATaskRunnerReset) { for (int i = 0; i < 2; ++i) { test::ScopedTaskEnvironment scoped_task_environment; // If the TaskRunner isn't released when the test::ScopedTaskEnvironment // goes out of scope, the second invocation of the line below will access a // deleted TaskScheduler and crash. g_com_sta_task_runner_user_visible.Get()->PostTask(FROM_HERE, DoNothing()); } } #endif // defined(OS_WIN) } // namespace base