Commit c92dedd2 authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

[threadpool] Support multiple fences active at the same time.

The goal is to support having a BEST_EFFORT execution fence prior to
main message loop start in BrowserMainLoop, and a fence when visible
tabs are loading in //chrome/browser/resource_coordinator. The lifetime
of both fences would overlap. The reason for not having a single fence
is that the first fence is controlled by //content whereas the second
fence is controlled by //chrome (precise tracking of whether a tab is
loading is a //chrome feature).

Bug: 839110, 1016825
Change-Id: Icaceee4cf9e5a1d4c52f9bb91e69e9f8125cee2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1872807
Commit-Queue: François Doray <fdoray@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711688}
parent 35396dc4
...@@ -350,17 +350,29 @@ void ThreadPoolImpl::JoinForTesting() { ...@@ -350,17 +350,29 @@ void ThreadPoolImpl::JoinForTesting() {
#endif #endif
} }
void ThreadPoolImpl::SetHasFence(bool has_fence) { void ThreadPoolImpl::BeginFence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(has_fence_, has_fence); ++num_fences_;
has_fence_ = has_fence;
UpdateCanRunPolicy(); UpdateCanRunPolicy();
} }
void ThreadPoolImpl::SetHasBestEffortFence(bool has_best_effort_fence) { void ThreadPoolImpl::EndFence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(has_best_effort_fence_, has_best_effort_fence); DCHECK_GT(num_fences_, 0);
has_best_effort_fence_ = has_best_effort_fence; --num_fences_;
UpdateCanRunPolicy();
}
void ThreadPoolImpl::BeginBestEffortFence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++num_best_effort_fences_;
UpdateCanRunPolicy();
}
void ThreadPoolImpl::EndBestEffortFence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(num_best_effort_fences_, 0);
--num_best_effort_fences_;
UpdateCanRunPolicy(); UpdateCanRunPolicy();
} }
...@@ -511,14 +523,14 @@ void ThreadPoolImpl::UpdateCanRunPolicy() { ...@@ -511,14 +523,14 @@ void ThreadPoolImpl::UpdateCanRunPolicy() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CanRunPolicy can_run_policy; CanRunPolicy can_run_policy;
if ((!has_fence_ && !has_best_effort_fence_ && if ((num_fences_ == 0 && num_best_effort_fences_ == 0 &&
!has_disable_best_effort_switch_) || !has_disable_best_effort_switch_) ||
task_tracker_->HasShutdownStarted()) { task_tracker_->HasShutdownStarted()) {
can_run_policy = CanRunPolicy::kAll; can_run_policy = CanRunPolicy::kAll;
} else if (has_fence_) { } else if (num_fences_ != 0) {
can_run_policy = CanRunPolicy::kNone; can_run_policy = CanRunPolicy::kNone;
} else { } else {
DCHECK(has_best_effort_fence_ || has_disable_best_effort_switch_); DCHECK(num_best_effort_fences_ > 0 || has_disable_best_effort_switch_);
can_run_policy = CanRunPolicy::kForegroundOnly; can_run_policy = CanRunPolicy::kForegroundOnly;
} }
......
...@@ -79,8 +79,10 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, ...@@ -79,8 +79,10 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
void FlushForTesting() override; void FlushForTesting() override;
void FlushAsyncForTesting(OnceClosure flush_callback) override; void FlushAsyncForTesting(OnceClosure flush_callback) override;
void JoinForTesting() override; void JoinForTesting() override;
void SetHasFence(bool has_fence) override; void BeginFence() override;
void SetHasBestEffortFence(bool has_best_effort_fence) override; void EndFence() override;
void BeginBestEffortFence() override;
void EndBestEffortFence() override;
// TaskExecutor: // TaskExecutor:
bool PostDelayedTask(const Location& from_here, bool PostDelayedTask(const Location& from_here,
...@@ -120,8 +122,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, ...@@ -120,8 +122,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
void ProcessRipeDelayedTasksForTesting(); void ProcessRipeDelayedTasksForTesting();
private: private:
// Invoked after |has_fence_| or |has_best_effort_fence_| is updated. Sets the // Invoked after |num_fences_| or |num_best_effort_fences_| is updated. Sets
// CanRunPolicy in TaskTracker and wakes up workers as appropriate. // the CanRunPolicy in TaskTracker and wakes up workers as appropriate.
void UpdateCanRunPolicy(); void UpdateCanRunPolicy();
// Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if // Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if
...@@ -170,10 +172,10 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, ...@@ -170,10 +172,10 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
// BEST_EFFORT tasks until shutdown. // BEST_EFFORT tasks until shutdown.
const bool has_disable_best_effort_switch_; const bool has_disable_best_effort_switch_;
// Whether a fence is preventing execution of tasks of any/BEST_EFFORT // Number of fences preventing execution of tasks of any/BEST_EFFORT priority.
// priority. Access controlled by |sequence_checker_|. // Access controlled by |sequence_checker_|.
bool has_fence_ = false; int num_fences_ = 0;
bool has_best_effort_fence_ = false; int num_best_effort_fences_ = 0;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// Set once JoinForTesting() has returned. // Set once JoinForTesting() has returned.
......
...@@ -222,7 +222,7 @@ class ThreadPostingTasks : public SimpleThread { ...@@ -222,7 +222,7 @@ class ThreadPostingTasks : public SimpleThread {
// Returns a vector with a TraitsExecutionModePair for each valid combination of // Returns a vector with a TraitsExecutionModePair for each valid combination of
// {ExecutionMode, TaskPriority, ThreadPolicy, MayBlock()}. // {ExecutionMode, TaskPriority, ThreadPolicy, MayBlock()}.
std::vector<TraitsExecutionModePair> GetTraitsExecutionModePair() { std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairs() {
std::vector<TraitsExecutionModePair> params; std::vector<TraitsExecutionModePair> params;
constexpr TaskSourceExecutionMode execution_modes[] = { constexpr TaskSourceExecutionMode execution_modes[] = {
...@@ -249,6 +249,21 @@ std::vector<TraitsExecutionModePair> GetTraitsExecutionModePair() { ...@@ -249,6 +249,21 @@ std::vector<TraitsExecutionModePair> GetTraitsExecutionModePair() {
return params; return params;
} }
// Returns a vector with enough TraitsExecutionModePairs to cover all valid
// combinations of task destination (background/foreground ThreadGroup,
// single-thread) and whether the task is affected by a BEST_EFFORT fence.
std::vector<TraitsExecutionModePair>
GetTraitsExecutionModePairsToCoverAllSchedulingOptions() {
return {TraitsExecutionModePair({ThreadPool(), TaskPriority::BEST_EFFORT},
TaskSourceExecutionMode::kSequenced),
TraitsExecutionModePair({ThreadPool(), TaskPriority::USER_BLOCKING},
TaskSourceExecutionMode::kSequenced),
TraitsExecutionModePair({ThreadPool(), TaskPriority::BEST_EFFORT},
TaskSourceExecutionMode::kSingleThread),
TraitsExecutionModePair({ThreadPool(), TaskPriority::USER_BLOCKING},
TaskSourceExecutionMode::kSingleThread)};
}
class ThreadPoolImplTestBase : public testing::Test { class ThreadPoolImplTestBase : public testing::Test {
public: public:
ThreadPoolImplTestBase() ThreadPoolImplTestBase()
...@@ -324,12 +339,15 @@ class ThreadPoolImplTest : public ThreadPoolImplTestBase, ...@@ -324,12 +339,15 @@ class ThreadPoolImplTest : public ThreadPoolImplTestBase,
DISALLOW_COPY_AND_ASSIGN(ThreadPoolImplTest); DISALLOW_COPY_AND_ASSIGN(ThreadPoolImplTest);
}; };
class ThreadPoolImplTestAllTraitsExecutionModes // Tests run for enough traits and execution mode combinations to cover all
// valid combinations of task destination (background/foreground ThreadGroup,
// single-thread) and whether the task is affected by a BEST_EFFORT fence.
class ThreadPoolImplTest_CoverAllSchedulingOptions
: public ThreadPoolImplTestBase, : public ThreadPoolImplTestBase,
public testing::WithParamInterface< public testing::WithParamInterface<
std::tuple<test::PoolType, TraitsExecutionModePair>> { std::tuple<test::PoolType, TraitsExecutionModePair>> {
public: public:
ThreadPoolImplTestAllTraitsExecutionModes() = default; ThreadPoolImplTest_CoverAllSchedulingOptions() = default;
test::PoolType GetPoolType() const override { test::PoolType GetPoolType() const override {
return std::get<0>(GetParam()); return std::get<0>(GetParam());
...@@ -340,7 +358,7 @@ class ThreadPoolImplTestAllTraitsExecutionModes ...@@ -340,7 +358,7 @@ class ThreadPoolImplTestAllTraitsExecutionModes
} }
private: private:
DISALLOW_COPY_AND_ASSIGN(ThreadPoolImplTestAllTraitsExecutionModes); DISALLOW_COPY_AND_ASSIGN(ThreadPoolImplTest_CoverAllSchedulingOptions);
}; };
} // namespace } // namespace
...@@ -348,7 +366,7 @@ class ThreadPoolImplTestAllTraitsExecutionModes ...@@ -348,7 +366,7 @@ class ThreadPoolImplTestAllTraitsExecutionModes
// Verifies that a Task posted via PostDelayedTask with parameterized TaskTraits // Verifies that a Task posted via PostDelayedTask with parameterized TaskTraits
// and no delay runs on a thread with the expected priority and I/O // and no delay runs on a thread with the expected priority and I/O
// restrictions. The ExecutionMode parameter is ignored by this test. // restrictions. The ExecutionMode parameter is ignored by this test.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskNoDelay) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskNoDelay) {
StartThreadPool(); StartThreadPool();
WaitableEvent task_ran; WaitableEvent task_ran;
thread_pool_->PostDelayedTask( thread_pool_->PostDelayedTask(
...@@ -363,7 +381,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskNoDelay) { ...@@ -363,7 +381,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskNoDelay) {
// TaskTraits and a non-zero delay runs on a thread with the expected priority // TaskTraits and a non-zero delay runs on a thread with the expected priority
// and I/O restrictions after the delay expires. The ExecutionMode parameter is // and I/O restrictions after the delay expires. The ExecutionMode parameter is
// ignored by this test. // ignored by this test.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskWithDelay) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskWithDelay) {
StartThreadPool(); StartThreadPool();
WaitableEvent task_ran; WaitableEvent task_ran;
thread_pool_->PostDelayedTask( thread_pool_->PostDelayedTask(
...@@ -378,7 +396,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskWithDelay) { ...@@ -378,7 +396,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostDelayedTaskWithDelay) {
// Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and // Verifies that Tasks posted via a TaskRunner with parameterized TaskTraits and
// ExecutionMode run on a thread with the expected priority and I/O restrictions // ExecutionMode run on a thread with the expected priority and I/O restrictions
// and respect the characteristics of their ExecutionMode. // and respect the characteristics of their ExecutionMode.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTasksViaTaskRunner) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTasksViaTaskRunner) {
StartThreadPool(); StartThreadPool();
test::TestTaskFactory factory( test::TestTaskFactory factory(
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
...@@ -398,7 +416,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTasksViaTaskRunner) { ...@@ -398,7 +416,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTasksViaTaskRunner) {
// Verifies that a task posted via PostDelayedTask without a delay doesn't run // Verifies that a task posted via PostDelayedTask without a delay doesn't run
// before Start() is called. // before Start() is called.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostDelayedTaskNoDelayBeforeStart) { PostDelayedTaskNoDelayBeforeStart) {
WaitableEvent task_running; WaitableEvent task_running;
thread_pool_->PostDelayedTask( thread_pool_->PostDelayedTask(
...@@ -420,7 +438,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, ...@@ -420,7 +438,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes,
// Verifies that a task posted via PostDelayedTask with a delay doesn't run // Verifies that a task posted via PostDelayedTask with a delay doesn't run
// before Start() is called. // before Start() is called.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostDelayedTaskWithDelayBeforeStart) { PostDelayedTaskWithDelayBeforeStart) {
WaitableEvent task_running; WaitableEvent task_running;
thread_pool_->PostDelayedTask( thread_pool_->PostDelayedTask(
...@@ -443,7 +461,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, ...@@ -443,7 +461,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes,
// Verifies that a task posted via a TaskRunner doesn't run before Start() is // Verifies that a task posted via a TaskRunner doesn't run before Start() is
// called. // called.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
PostTaskViaTaskRunnerBeforeStart) { PostTaskViaTaskRunnerBeforeStart) {
WaitableEvent task_running; WaitableEvent task_running;
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
...@@ -467,7 +485,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, ...@@ -467,7 +485,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes,
// Verify that posting tasks after the thread pool was destroyed fails but // Verify that posting tasks after the thread pool was destroyed fails but
// doesn't crash. // doesn't crash.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTaskAfterDestroy) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTaskAfterDestroy) {
StartThreadPool(); StartThreadPool();
auto task_runner = CreateTaskRunnerAndExecutionMode( auto task_runner = CreateTaskRunnerAndExecutionMode(
...@@ -483,7 +501,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTaskAfterDestroy) { ...@@ -483,7 +501,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, PostTaskAfterDestroy) {
// Verify that all tasks posted to a TaskRunner after Start() run in a // Verify that all tasks posted to a TaskRunner after Start() run in a
// USER_BLOCKING environment when the AllTasksUserBlocking variation param of // USER_BLOCKING environment when the AllTasksUserBlocking variation param of
// the BrowserScheduler experiment is true. // the BrowserScheduler experiment is true.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
AllTasksAreUserBlockingTaskRunner) { AllTasksAreUserBlockingTaskRunner) {
TaskTraits user_blocking_traits = GetTraits(); TaskTraits user_blocking_traits = GetTraits();
user_blocking_traits.UpdatePriority(TaskPriority::USER_BLOCKING); user_blocking_traits.UpdatePriority(TaskPriority::USER_BLOCKING);
...@@ -503,7 +521,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, ...@@ -503,7 +521,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes,
// Verify that all tasks posted via PostDelayedTask() after Start() run in a // Verify that all tasks posted via PostDelayedTask() after Start() run in a
// USER_BLOCKING environment when the AllTasksUserBlocking variation param of // USER_BLOCKING environment when the AllTasksUserBlocking variation param of
// the BrowserScheduler experiment is true. // the BrowserScheduler experiment is true.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, AllTasksAreUserBlocking) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, AllTasksAreUserBlocking) {
TaskTraits user_blocking_traits = GetTraits(); TaskTraits user_blocking_traits = GetTraits();
user_blocking_traits.UpdatePriority(TaskPriority::USER_BLOCKING); user_blocking_traits.UpdatePriority(TaskPriority::USER_BLOCKING);
...@@ -522,7 +540,8 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, AllTasksAreUserBlocking) { ...@@ -522,7 +540,8 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, AllTasksAreUserBlocking) {
// Verifies that FlushAsyncForTesting() calls back correctly for all trait and // Verifies that FlushAsyncForTesting() calls back correctly for all trait and
// execution mode pairs. // execution mode pairs.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, FlushAsyncForTestingSimple) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
FlushAsyncForTestingSimple) {
StartThreadPool(); StartThreadPool();
WaitableEvent unblock_task; WaitableEvent unblock_task;
...@@ -579,8 +598,8 @@ TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) { ...@@ -579,8 +598,8 @@ TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) {
PlatformThread::Sleep(TestTimeouts::tiny_timeout()); PlatformThread::Sleep(TestTimeouts::tiny_timeout());
// The BEST_EFFORT task should not run when a BEST_EFFORT fence is deleted. // The BEST_EFFORT task should not run when a BEST_EFFORT fence is deleted.
thread_pool.SetHasBestEffortFence(true); thread_pool.BeginBestEffortFence();
thread_pool.SetHasBestEffortFence(false); thread_pool.EndBestEffortFence();
PlatformThread::Sleep(TestTimeouts::tiny_timeout()); PlatformThread::Sleep(TestTimeouts::tiny_timeout());
...@@ -591,13 +610,63 @@ TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) { ...@@ -591,13 +610,63 @@ TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) {
thread_pool.JoinForTesting(); thread_pool.JoinForTesting();
} }
// Verifies that tasks only run when allowed by SetHasFence(). // Verifies that tasks only run when allowed by fences.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFence) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, Fence) {
StartThreadPool();
AtomicFlag can_run;
WaitableEvent 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();
}
// Verifies that multiple fences can exist at the same time.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleFences) {
StartThreadPool();
AtomicFlag can_run;
WaitableEvent 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());
// The task can only run when both fences are removed.
can_run.Set();
thread_pool_->EndFence();
did_run.Wait();
}
// Verifies that a call to BeginFence() before Start() is honored.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, FenceBeforeStart) {
thread_pool_->BeginFence();
StartThreadPool(); StartThreadPool();
AtomicFlag can_run; AtomicFlag can_run;
WaitableEvent did_run; WaitableEvent did_run;
thread_pool_->SetHasFence(true);
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode()) GetExecutionMode())
...@@ -609,21 +678,22 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFence) { ...@@ -609,21 +678,22 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFence) {
PlatformThread::Sleep(TestTimeouts::tiny_timeout()); PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set(); can_run.Set();
thread_pool_->SetHasFence(false); thread_pool_->EndFence();
did_run.Wait(); did_run.Wait();
} }
// Verifies that a call to SetHasFence(true) before Start() is honored. // Verifies that tasks only run when allowed by BEST_EFFORT fences.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFenceBeforeStart) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, BestEffortFence) {
thread_pool_->SetHasFence(true);
StartThreadPool(); StartThreadPool();
AtomicFlag can_run; AtomicFlag can_run;
WaitableEvent did_run; WaitableEvent did_run;
thread_pool_->BeginBestEffortFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode()) GetExecutionMode())
->PostTask(FROM_HERE, BindLambdaForTesting([&]() { ->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
if (GetTraits().priority() == TaskPriority::BEST_EFFORT)
EXPECT_TRUE(can_run.IsSet()); EXPECT_TRUE(can_run.IsSet());
did_run.Signal(); did_run.Signal();
})); }));
...@@ -631,18 +701,18 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFenceBeforeStart) { ...@@ -631,18 +701,18 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasFenceBeforeStart) {
PlatformThread::Sleep(TestTimeouts::tiny_timeout()); PlatformThread::Sleep(TestTimeouts::tiny_timeout());
can_run.Set(); can_run.Set();
thread_pool_->SetHasFence(false); thread_pool_->EndBestEffortFence();
did_run.Wait(); did_run.Wait();
} }
// Verifies that BEST_EFFORT tasks only run when allowed by // Verifies that multiple BEST_EFFORT fences can exist at the same time.
// SetHasBestEffortFence(). TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, MultipleBestEffortFences) {
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasBestEffortFence) {
StartThreadPool(); StartThreadPool();
AtomicFlag can_run; AtomicFlag can_run;
WaitableEvent did_run; WaitableEvent did_run;
thread_pool_->SetHasBestEffortFence(true); thread_pool_->BeginBestEffortFence();
thread_pool_->BeginBestEffortFence();
CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(),
GetExecutionMode()) GetExecutionMode())
...@@ -654,8 +724,37 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasBestEffortFence) { ...@@ -654,8 +724,37 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasBestEffortFence) {
PlatformThread::Sleep(TestTimeouts::tiny_timeout()); PlatformThread::Sleep(TestTimeouts::tiny_timeout());
thread_pool_->EndBestEffortFence();
PlatformThread::Sleep(TestTimeouts::tiny_timeout());
// The task can only run when both fences are removed.
can_run.Set(); can_run.Set();
thread_pool_->SetHasBestEffortFence(false); thread_pool_->EndBestEffortFence();
did_run.Wait();
}
// Verifies that a call to BeginBestEffortFence() before Start() is honored.
TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
BestEffortFenceBeforeStart) {
thread_pool_->BeginBestEffortFence();
StartThreadPool();
AtomicFlag can_run;
WaitableEvent 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(); did_run.Wait();
} }
...@@ -666,7 +765,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasBestEffortFence) { ...@@ -666,7 +765,7 @@ TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, SetHasBestEffortFence) {
TEST_P(ThreadPoolImplTest, MultipleTraitsExecutionModePair) { TEST_P(ThreadPoolImplTest, MultipleTraitsExecutionModePair) {
StartThreadPool(); StartThreadPool();
std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks; std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
for (const auto& test_params : GetTraitsExecutionModePair()) { for (const auto& test_params : GetTraitsExecutionModePairs()) {
threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>( threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>(
thread_pool_.get(), test_params.traits, GetPoolType(), thread_pool_.get(), test_params.traits, GetPoolType(),
test_params.execution_mode)); test_params.execution_mode));
...@@ -1193,7 +1292,8 @@ class MustBeDestroyed { ...@@ -1193,7 +1292,8 @@ class MustBeDestroyed {
} // namespace } // namespace
// Regression test for https://crbug.com/945087. // Regression test for https://crbug.com/945087.
TEST_P(ThreadPoolImplTestAllTraitsExecutionModes, NoLeakWhenPostingNestedTask) { TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions,
NoLeakWhenPostingNestedTask) {
StartThreadPool(); StartThreadPool();
SequenceLocalStorageSlot<std::unique_ptr<MustBeDestroyed>> sls; SequenceLocalStorageSlot<std::unique_ptr<MustBeDestroyed>> sls;
...@@ -1307,10 +1407,10 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test, ...@@ -1307,10 +1407,10 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test,
CreateTaskRunnersAndEvents(test->thread_pool_.get(), thread_policy); CreateTaskRunnersAndEvents(test->thread_pool_.get(), thread_policy);
// Prevent tasks from running. // Prevent tasks from running.
test->thread_pool_->SetHasFence(true); test->thread_pool_->BeginFence();
// Post tasks to multiple task runners while they are at initial priority. // Post tasks to multiple task runners while they are at initial priority.
// They won't run immediately because of the call to SetHasFence(true) above. // They won't run immediately because of the call to BeginFence() above.
for (auto& task_runner_and_events : task_runners_and_events) { for (auto& task_runner_and_events : task_runners_and_events) {
task_runner_and_events->task_runner->PostTask( task_runner_and_events->task_runner->PostTask(
FROM_HERE, FROM_HERE,
...@@ -1339,7 +1439,7 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test, ...@@ -1339,7 +1439,7 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test,
} }
// Allow tasks to run. // Allow tasks to run.
test->thread_pool_->SetHasFence(false); test->thread_pool_->EndFence();
for (auto& task_runner_and_events : task_runners_and_events) for (auto& task_runner_and_events : task_runners_and_events)
test::WaitWithoutBlockingObserver(&task_runner_and_events->task_ran); test::WaitWithoutBlockingObserver(&task_runner_and_events->task_ran);
...@@ -1435,25 +1535,24 @@ TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) { ...@@ -1435,25 +1535,24 @@ TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) {
} }
} }
INSTANTIATE_TEST_SUITE_P(, auto GetPoolValues() {
ThreadPoolImplTest, return ::testing::Values(test::PoolType::GENERIC
::testing::Values(test::PoolType::GENERIC
#if HAS_NATIVE_THREAD_POOL() #if HAS_NATIVE_THREAD_POOL()
, ,
test::PoolType::NATIVE test::PoolType::NATIVE
#endif #endif
)); );
}
INSTANTIATE_TEST_SUITE_P(, ThreadPoolImplTest, GetPoolValues());
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
, ,
ThreadPoolImplTestAllTraitsExecutionModes, ThreadPoolImplTest_CoverAllSchedulingOptions,
::testing::Combine(::testing::Values(test::PoolType::GENERIC ::testing::Combine(
#if HAS_NATIVE_THREAD_POOL() GetPoolValues(),
, ::testing::ValuesIn(
test::PoolType::NATIVE GetTraitsExecutionModePairsToCoverAllSchedulingOptions())));
#endif
),
::testing::ValuesIn(GetTraitsExecutionModePair())));
} // namespace internal } // namespace internal
} // namespace base } // namespace base
...@@ -29,24 +29,24 @@ ThreadPoolInstance::InitParams::~InitParams() = default; ...@@ -29,24 +29,24 @@ ThreadPoolInstance::InitParams::~InitParams() = default;
ThreadPoolInstance::ScopedExecutionFence::ScopedExecutionFence() { ThreadPoolInstance::ScopedExecutionFence::ScopedExecutionFence() {
DCHECK(g_thread_pool); DCHECK(g_thread_pool);
g_thread_pool->SetHasFence(true); g_thread_pool->BeginFence();
} }
ThreadPoolInstance::ScopedExecutionFence::~ScopedExecutionFence() { ThreadPoolInstance::ScopedExecutionFence::~ScopedExecutionFence() {
DCHECK(g_thread_pool); DCHECK(g_thread_pool);
g_thread_pool->SetHasFence(false); g_thread_pool->EndFence();
} }
ThreadPoolInstance::ScopedBestEffortExecutionFence:: ThreadPoolInstance::ScopedBestEffortExecutionFence::
ScopedBestEffortExecutionFence() { ScopedBestEffortExecutionFence() {
DCHECK(g_thread_pool); DCHECK(g_thread_pool);
g_thread_pool->SetHasBestEffortFence(true); g_thread_pool->BeginBestEffortFence();
} }
ThreadPoolInstance::ScopedBestEffortExecutionFence:: ThreadPoolInstance::ScopedBestEffortExecutionFence::
~ScopedBestEffortExecutionFence() { ~ScopedBestEffortExecutionFence() {
DCHECK(g_thread_pool); DCHECK(g_thread_pool);
g_thread_pool->SetHasBestEffortFence(false); g_thread_pool->EndBestEffortFence();
} }
#if !defined(OS_NACL) #if !defined(OS_NACL)
......
...@@ -100,11 +100,12 @@ class BASE_EXPORT ThreadPoolInstance { ...@@ -100,11 +100,12 @@ class BASE_EXPORT ThreadPoolInstance {
}; };
// A Scoped(BestEffort)ExecutionFence prevents new tasks of any/BEST_EFFORT // A Scoped(BestEffort)ExecutionFence prevents new tasks of any/BEST_EFFORT
// priority from being scheduled in ThreadPoolInstance within its scope. Upon // priority from being scheduled in ThreadPoolInstance within its scope.
// its destruction, tasks that were preeempted are released. Note: the // Multiple fences can exist at the same time. Upon destruction of all
// constructor of Scoped(BestEffort)ExecutionFence will not wait for currently // Scoped(BestEffort)ExecutionFences, tasks that were preeempted are released.
// running tasks (as they were posted before entering this scope and do not // Note: the constructor of Scoped(BestEffort)ExecutionFence will not wait for
// violate the contract; some of them could be CONTINUE_ON_SHUTDOWN and // currently running tasks (as they were posted before entering this scope and
// do not violate the contract; some of them could be CONTINUE_ON_SHUTDOWN and
// waiting for them to complete is ill-advised). // waiting for them to complete is ill-advised).
class BASE_EXPORT ScopedExecutionFence { class BASE_EXPORT ScopedExecutionFence {
public: public:
...@@ -244,10 +245,12 @@ class BASE_EXPORT ThreadPoolInstance { ...@@ -244,10 +245,12 @@ class BASE_EXPORT ThreadPoolInstance {
virtual int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( virtual int GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
const TaskTraits& traits) const = 0; const TaskTraits& traits) const = 0;
// Sets whether a fence prevents execution of tasks of any / BEST_EFFORT // Starts/stops a fence that prevents execution of tasks of any / BEST_EFFORT
// priority. // priority.
virtual void SetHasFence(bool can_run) = 0; virtual void BeginFence() = 0;
virtual void SetHasBestEffortFence(bool can_run) = 0; virtual void EndFence() = 0;
virtual void BeginBestEffortFence() = 0;
virtual void EndBestEffortFence() = 0;
}; };
} // namespace base } // namespace base
......
...@@ -12,25 +12,28 @@ namespace base { ...@@ -12,25 +12,28 @@ namespace base {
class ThreadPoolTestHelpers { class ThreadPoolTestHelpers {
public: public:
// Enables/disables an execution fence that prevents tasks from running. // Enables/disables an execution fence that prevents tasks from running.
static void SetThreadPoolExecutionFenceEnabledForTesting(bool has_fence); static void BeginFenceForTesting();
static void EndFenceForTesting();
}; };
// static // static
void ThreadPoolTestHelpers::SetThreadPoolExecutionFenceEnabledForTesting( void ThreadPoolTestHelpers::BeginFenceForTesting() {
bool has_fence) { ThreadPoolInstance::Get()->BeginFence();
ThreadPoolInstance::Get()->SetHasFence(has_fence); }
// static
void ThreadPoolTestHelpers::EndFenceForTesting() {
ThreadPoolInstance::Get()->EndFence();
} }
} // namespace base } // namespace base
void JNI_ThreadPoolTestHelpers_EnableThreadPoolExecutionForTesting( void JNI_ThreadPoolTestHelpers_EnableThreadPoolExecutionForTesting(
JNIEnv* env) { JNIEnv* env) {
base::ThreadPoolTestHelpers:: base::ThreadPoolTestHelpers::EndFenceForTesting();
SetThreadPoolExecutionFenceEnabledForTesting(false);
} }
void JNI_ThreadPoolTestHelpers_DisableThreadPoolExecutionForTesting( void JNI_ThreadPoolTestHelpers_DisableThreadPoolExecutionForTesting(
JNIEnv* env) { JNIEnv* env) {
base::ThreadPoolTestHelpers:: base::ThreadPoolTestHelpers::BeginFenceForTesting();
SetThreadPoolExecutionFenceEnabledForTesting(true);
} }
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment