// Copyright 2019 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. #ifndef BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_ #define BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_ #include "base/synchronization/atomic_flag.h" #include "base/task/thread_pool/task_tracker.h" #include "base/task/thread_pool/test_utils.h" #include "base/task_runner.h" #include "base/test/bind_test_util.h" #include "base/test/test_timeouts.h" #include "base/test/test_waitable_event.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" namespace base { namespace internal { namespace test { // Verify that tasks only run when allowed by the CanRunPolicy. |target| is the // object on which DidUpdateCanRunPolicy() must be called after updating the // CanRunPolicy in |task_tracker|. |create_task_runner| is a function that // receives a TaskPriority and returns a TaskRunner. |task_tracker| is the // TaskTracker. template void TestCanRunPolicyBasic(Target* target, CreateTaskRunner create_task_runner, TaskTracker* task_tracker) { AtomicFlag foreground_can_run; TestWaitableEvent foreground_did_run; AtomicFlag best_effort_can_run; TestWaitableEvent best_effort_did_run; task_tracker->SetCanRunPolicy(CanRunPolicy::kNone); target->DidUpdateCanRunPolicy(); const auto user_visible_task_runner = create_task_runner(TaskPriority::USER_VISIBLE); user_visible_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() { EXPECT_TRUE(foreground_can_run.IsSet()); foreground_did_run.Signal(); })); const auto best_effort_task_runner = create_task_runner(TaskPriority::BEST_EFFORT); best_effort_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() { EXPECT_TRUE(best_effort_can_run.IsSet()); best_effort_did_run.Signal(); })); PlatformThread::Sleep(TestTimeouts::tiny_timeout()); foreground_can_run.Set(); task_tracker->SetCanRunPolicy(CanRunPolicy::kForegroundOnly); target->DidUpdateCanRunPolicy(); foreground_did_run.Wait(); PlatformThread::Sleep(TestTimeouts::tiny_timeout()); best_effort_can_run.Set(); task_tracker->SetCanRunPolicy(CanRunPolicy::kAll); target->DidUpdateCanRunPolicy(); best_effort_did_run.Wait(); } // Verify that if a task was allowed to run by the CanRunPolicy when it was // posted, but the CanRunPolicy is updated to disallow it from running before it // starts running, it doesn't run. |target| is the object on which // DidUpdateCanRunPolicy() must be called after updating the CanRunPolicy in // |task_tracker|. |create_task_runner| is a function that receives a // TaskPriority and returns a *Sequenced*TaskRunner. |task_tracker| is the // TaskTracker. template void TestCanRunPolicyChangedBeforeRun(Target* target, CreateTaskRunner create_task_runner, TaskTracker* task_tracker) { constexpr struct { // Descriptor for the test case. const char* descriptor; // Task priority being tested. TaskPriority priority; // Policy that disallows running tasks with |priority|. CanRunPolicy disallow_policy; // Policy that allows running tasks with |priority|. CanRunPolicy allow_policy; } kTestCases[] = { {"BestEffort/kNone/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kNone, CanRunPolicy::kAll}, {"BestEffort/kForegroundOnly/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kForegroundOnly, CanRunPolicy::kAll}, {"UserVisible/kNone/kForegroundOnly", TaskPriority::USER_VISIBLE, CanRunPolicy::kNone, CanRunPolicy::kForegroundOnly}, {"UserVisible/kNone/kAll", TaskPriority::USER_VISIBLE, CanRunPolicy::kNone, CanRunPolicy::kAll}}; for (auto& test_case : kTestCases) { SCOPED_TRACE(test_case.descriptor); TestWaitableEvent first_task_started; TestWaitableEvent first_task_blocked; AtomicFlag second_task_can_run; task_tracker->SetCanRunPolicy(test_case.allow_policy); target->DidUpdateCanRunPolicy(); const auto task_runner = create_task_runner(test_case.priority); task_runner->PostTask( FROM_HERE, BindLambdaForTesting([&]() { first_task_started.Signal(); first_task_blocked.Wait(); })); task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() { EXPECT_TRUE(second_task_can_run.IsSet()); })); first_task_started.Wait(); task_tracker->SetCanRunPolicy(test_case.disallow_policy); target->DidUpdateCanRunPolicy(); first_task_blocked.Signal(); PlatformThread::Sleep(TestTimeouts::tiny_timeout()); second_task_can_run.Set(); task_tracker->SetCanRunPolicy(test_case.allow_policy); target->DidUpdateCanRunPolicy(); task_tracker->FlushForTesting(); } } // Regression test for https://crbug.com/950383 template void TestCanRunPolicyLoad(Target* target, CreateTaskRunner create_task_runner, TaskTracker* task_tracker) { constexpr struct { // Descriptor for the test case. const char* descriptor; // Task priority being tested. TaskPriority priority; // Policy that allows running tasks with |priority|. CanRunPolicy allow_policy; // Policy that disallows running tasks with |priority|. CanRunPolicy disallow_policy; } kTestCases[] = { {"BestEffort/kAll/kNone", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll, CanRunPolicy::kNone}, {"BestEffort/kAll/kForegroundOnly", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll, CanRunPolicy::kForegroundOnly}, {"UserVisible/kForegroundOnly/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kForegroundOnly, CanRunPolicy::kNone}, {"UserVisible/kAll/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kAll, CanRunPolicy::kNone}}; for (auto& test_case : kTestCases) { SCOPED_TRACE(test_case.descriptor); task_tracker->SetCanRunPolicy(test_case.allow_policy); target->DidUpdateCanRunPolicy(); const auto task_runner = create_task_runner(test_case.priority); // Post less tasks on iOS to avoid timeouts. const size_t kLargeNumber = #if defined(OS_IOS) 16; #else 256; #endif for (size_t i = 0; i < kLargeNumber; ++i) task_runner->PostTask(FROM_HERE, DoNothing()); // Change the CanRunPolicy concurrently with running tasks. // This should not cause crashes. for (size_t i = 0; i < kLargeNumber; ++i) { task_tracker->SetCanRunPolicy(test_case.disallow_policy); target->DidUpdateCanRunPolicy(); task_tracker->SetCanRunPolicy(test_case.allow_policy); target->DidUpdateCanRunPolicy(); } task_tracker->FlushForTesting(); } } } // namespace test } // namespace internal } // namespace base #endif // BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_