blob: 8f9d8a17a38ec3c2bddec8fc27d3332a2eeaa5d3 [file] [log] [blame]
// 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/synchronization/waitable_event.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/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 <typename Target, typename CreateTaskRunner>
void TestCanRunPolicyBasic(Target* target,
CreateTaskRunner create_task_runner,
TaskTracker* task_tracker) {
AtomicFlag foreground_can_run;
WaitableEvent foreground_did_run;
AtomicFlag best_effort_can_run;
WaitableEvent 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 <typename Target, typename CreateTaskRunner>
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);
WaitableEvent first_task_started;
WaitableEvent 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();
test::WaitWithoutBlockingObserver(&first_task_blocked);
}));
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 <typename Target, typename CreateTaskRunner>
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_