| Index: base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
|
| diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ea9cb66a21c3371798638d93a51cb60818cee2c2
|
| --- /dev/null
|
| +++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
|
| @@ -0,0 +1,357 @@
|
| +// 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/scheduler_single_thread_task_runner_manager.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/synchronization/lock.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/task_scheduler/delayed_task_manager.h"
|
| +#include "base/task_scheduler/post_task.h"
|
| +#include "base/task_scheduler/scheduler_worker_pool_params.h"
|
| +#include "base/task_scheduler/task_tracker.h"
|
| +#include "base/task_scheduler/task_traits.h"
|
| +#include "base/test/test_timeouts.h"
|
| +#include "base/threading/simple_thread.h"
|
| +#include "base/threading/thread.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace base {
|
| +namespace internal {
|
| +
|
| +namespace {
|
| +
|
| +enum WorkerPoolType : size_t {
|
| + BACKGROUND_WORKER_POOL = 0,
|
| + FOREGROUND_WORKER_POOL,
|
| +};
|
| +
|
| +static size_t GetThreadPoolIndexForTraits(const TaskTraits& traits) {
|
| + return traits.priority() == TaskPriority::BACKGROUND ? BACKGROUND_WORKER_POOL
|
| + : FOREGROUND_WORKER_POOL;
|
| +}
|
| +
|
| +std::vector<SchedulerWorkerPoolParams> GetParamsVector() {
|
| + using StandbyThreadPolicy = SchedulerWorkerPoolParams::StandbyThreadPolicy;
|
| +
|
| + std::vector<SchedulerWorkerPoolParams> params_vector;
|
| +
|
| + DCHECK_EQ(BACKGROUND_WORKER_POOL, params_vector.size());
|
| + params_vector.emplace_back("Background", ThreadPriority::BACKGROUND,
|
| + StandbyThreadPolicy::LAZY, 1U, TimeDelta::Max());
|
| +
|
| + DCHECK_EQ(FOREGROUND_WORKER_POOL, params_vector.size());
|
| + params_vector.emplace_back("Foreground", ThreadPriority::NORMAL,
|
| + StandbyThreadPolicy::LAZY, 1U, TimeDelta::Max());
|
| +
|
| + return params_vector;
|
| +}
|
| +
|
| +class TaskSchedulerSingleThreadTaskRunnerManagerTest : public testing::Test {
|
| + public:
|
| + TaskSchedulerSingleThreadTaskRunnerManagerTest()
|
| + : service_thread_("TaskSchedulerServiceThread") {}
|
| +
|
| + void SetUp() override {
|
| + service_thread_.Start();
|
| +
|
| + delayed_task_manager_ =
|
| + MakeUnique<DelayedTaskManager>(service_thread_.task_runner());
|
| + single_thread_task_runner_manager_ =
|
| + MakeUnique<SchedulerSingleThreadTaskRunnerManager>(
|
| + GetParamsVector(), Bind(&GetThreadPoolIndexForTraits),
|
| + &task_tracker_, delayed_task_manager_.get());
|
| + }
|
| +
|
| + void TearDown() override {
|
| + TearDownSingleThreadTaskRunnerManager();
|
| + delayed_task_manager_.reset();
|
| + service_thread_.Stop();
|
| + }
|
| +
|
| + protected:
|
| + virtual void TearDownSingleThreadTaskRunnerManager() {
|
| + single_thread_task_runner_manager_->JoinForTesting();
|
| + single_thread_task_runner_manager_.reset();
|
| + }
|
| +
|
| + std::unique_ptr<SchedulerSingleThreadTaskRunnerManager>
|
| + single_thread_task_runner_manager_;
|
| + TaskTracker task_tracker_;
|
| +
|
| + private:
|
| + Thread service_thread_;
|
| + std::unique_ptr<DelayedTaskManager> delayed_task_manager_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerTest);
|
| +};
|
| +
|
| +void CaptureThreadRef(PlatformThreadRef* thread_ref) {
|
| + ASSERT_TRUE(thread_ref);
|
| + *thread_ref = PlatformThread::CurrentRef();
|
| +}
|
| +
|
| +void CaptureThreadPriority(ThreadPriority* thread_priority) {
|
| + ASSERT_TRUE(thread_priority);
|
| + *thread_priority = PlatformThread::GetCurrentThreadPriority();
|
| +}
|
| +
|
| +void ShouldNotRun() {
|
| + ADD_FAILURE() << "Ran a task that shouldn't run.";
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, DifferentThreadsUsed) {
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithShutdownBehavior(
|
| + TaskShutdownBehavior::BLOCK_SHUTDOWN));
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithShutdownBehavior(
|
| + TaskShutdownBehavior::BLOCK_SHUTDOWN));
|
| +
|
| + PlatformThreadRef thread_ref_1;
|
| + task_runner_1->PostTask(FROM_HERE, Bind(&CaptureThreadRef, &thread_ref_1));
|
| + PlatformThreadRef thread_ref_2;
|
| + task_runner_2->PostTask(FROM_HERE, Bind(&CaptureThreadRef, &thread_ref_2));
|
| +
|
| + task_tracker_.Shutdown();
|
| +
|
| + ASSERT_FALSE(thread_ref_1.is_null());
|
| + ASSERT_FALSE(thread_ref_2.is_null());
|
| + EXPECT_NE(thread_ref_1, thread_ref_2);
|
| +}
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, PrioritySetCorrectly) {
|
| + // Why are events used here instead of the task tracker?
|
| + // Shutting down can cause priorities to get raised. This means we have to use
|
| + // events to determine when a task is run.
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_background =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithPriority(TaskPriority::BACKGROUND));
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_user_visible =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithPriority(TaskPriority::USER_VISIBLE));
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_user_blocking =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits()
|
| + .WithPriority(TaskPriority::USER_BLOCKING)
|
| + .WithShutdownBehavior(TaskShutdownBehavior::BLOCK_SHUTDOWN));
|
| +
|
| + ThreadPriority thread_priority_background;
|
| + task_runner_background->PostTask(
|
| + FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_background));
|
| + WaitableEvent waitable_event_background(
|
| + WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + task_runner_background->PostTask(
|
| + FROM_HERE,
|
| + Bind(&WaitableEvent::Signal, Unretained(&waitable_event_background)));
|
| +
|
| + ThreadPriority thread_priority_user_visible;
|
| + task_runner_user_visible->PostTask(
|
| + FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_user_visible));
|
| + WaitableEvent waitable_event_user_visible(
|
| + WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + task_runner_user_visible->PostTask(
|
| + FROM_HERE,
|
| + Bind(&WaitableEvent::Signal, Unretained(&waitable_event_user_visible)));
|
| +
|
| + ThreadPriority thread_priority_user_blocking;
|
| + task_runner_user_blocking->PostTask(
|
| + FROM_HERE, Bind(&CaptureThreadPriority, &thread_priority_user_blocking));
|
| + WaitableEvent waitable_event_user_blocking(
|
| + WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + task_runner_user_blocking->PostTask(
|
| + FROM_HERE,
|
| + Bind(&WaitableEvent::Signal, Unretained(&waitable_event_user_blocking)));
|
| +
|
| + waitable_event_background.Wait();
|
| + waitable_event_user_visible.Wait();
|
| + waitable_event_user_blocking.Wait();
|
| +
|
| + if (Lock::HandlesMultipleThreadPriorities())
|
| + EXPECT_EQ(ThreadPriority::BACKGROUND, thread_priority_background);
|
| + else
|
| + EXPECT_EQ(ThreadPriority::NORMAL, thread_priority_background);
|
| + EXPECT_EQ(ThreadPriority::NORMAL, thread_priority_user_visible);
|
| + EXPECT_EQ(ThreadPriority::NORMAL, thread_priority_user_blocking);
|
| +}
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, PostTaskAfterShutdown) {
|
| + auto task_runner = single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits());
|
| + task_tracker_.Shutdown();
|
| + EXPECT_FALSE(task_runner->PostTask(FROM_HERE, Bind(&ShouldNotRun)));
|
| +}
|
| +
|
| +// Verify that a Task runs shortly after its delay expires.
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest, PostDelayedTask) {
|
| + TimeTicks start_time = TimeTicks::Now();
|
| +
|
| + // Post a task with a short delay.
|
| + WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + auto task_runner = single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(TaskTraits());
|
| + EXPECT_TRUE(task_runner->PostDelayedTask(
|
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_ran)),
|
| + TestTimeouts::tiny_timeout()));
|
| +
|
| + // Wait until the task runs.
|
| + task_ran.Wait();
|
| +
|
| + // Expect the task to run after its delay expires, but not more than 250 ms
|
| + // after that.
|
| + const TimeDelta actual_delay = TimeTicks::Now() - start_time;
|
| + EXPECT_GE(actual_delay, TestTimeouts::tiny_timeout());
|
| + EXPECT_LT(actual_delay,
|
| + TimeDelta::FromMilliseconds(250) + TestTimeouts::tiny_timeout());
|
| +}
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerTest,
|
| + RunsTasksOnCurrentThread) {
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_1 =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithShutdownBehavior(
|
| + TaskShutdownBehavior::BLOCK_SHUTDOWN));
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_2 =
|
| + single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithShutdownBehavior(
|
| + TaskShutdownBehavior::BLOCK_SHUTDOWN));
|
| +
|
| + EXPECT_FALSE(task_runner_1->RunsTasksOnCurrentThread());
|
| + EXPECT_FALSE(task_runner_2->RunsTasksOnCurrentThread());
|
| +
|
| + task_runner_1->PostTask(
|
| + FROM_HERE, Bind(
|
| + [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
|
| + EXPECT_TRUE(task_runner_1->RunsTasksOnCurrentThread());
|
| + EXPECT_FALSE(task_runner_2->RunsTasksOnCurrentThread());
|
| + },
|
| + task_runner_1, task_runner_2));
|
| +
|
| + task_runner_2->PostTask(
|
| + FROM_HERE, Bind(
|
| + [](scoped_refptr<SingleThreadTaskRunner> task_runner_1,
|
| + scoped_refptr<SingleThreadTaskRunner> task_runner_2) {
|
| + EXPECT_FALSE(task_runner_1->RunsTasksOnCurrentThread());
|
| + EXPECT_TRUE(task_runner_2->RunsTasksOnCurrentThread());
|
| + },
|
| + task_runner_1, task_runner_2));
|
| +
|
| + task_tracker_.Shutdown();
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +class CallJoinFromDifferentThread : public SimpleThread {
|
| + public:
|
| + CallJoinFromDifferentThread(
|
| + SchedulerSingleThreadTaskRunnerManager* manager_to_join)
|
| + : SimpleThread("SchedulerSingleThreadTaskRunnerManagerJoinThread"),
|
| + manager_to_join_(manager_to_join),
|
| + run_started_event_(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED) {}
|
| +
|
| + ~CallJoinFromDifferentThread() override = default;
|
| +
|
| + void Run() override {
|
| + run_started_event_.Signal();
|
| + manager_to_join_->JoinForTesting();
|
| + }
|
| +
|
| + void WaitForRunToStart() { run_started_event_.Wait(); }
|
| +
|
| + private:
|
| + SchedulerSingleThreadTaskRunnerManager* const manager_to_join_;
|
| + WaitableEvent run_started_event_;
|
| + DISALLOW_COPY_AND_ASSIGN(CallJoinFromDifferentThread);
|
| +};
|
| +
|
| +class TaskSchedulerSingleThreadTaskRunnerManagerJoinTest
|
| + : public TaskSchedulerSingleThreadTaskRunnerManagerTest {
|
| + public:
|
| + TaskSchedulerSingleThreadTaskRunnerManagerJoinTest() = default;
|
| + ~TaskSchedulerSingleThreadTaskRunnerManagerJoinTest() override = default;
|
| +
|
| + protected:
|
| + void TearDownSingleThreadTaskRunnerManager() override {
|
| + // The tests themselves are responsible for calling JoinForTesting().
|
| + single_thread_task_runner_manager_.reset();
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest, ConcurrentJoin) {
|
| + WaitableEvent task_running(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + WaitableEvent task_blocking(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| +
|
| + {
|
| + auto task_runner = single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithBaseSyncPrimitives());
|
| + EXPECT_TRUE(task_runner->PostTask(
|
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_running))));
|
| + EXPECT_TRUE(task_runner->PostTask(
|
| + FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&task_blocking))));
|
| + }
|
| +
|
| + task_running.Wait();
|
| + CallJoinFromDifferentThread join_from_different_thread(
|
| + single_thread_task_runner_manager_.get());
|
| + join_from_different_thread.Start();
|
| + join_from_different_thread.WaitForRunToStart();
|
| + task_blocking.Signal();
|
| + join_from_different_thread.Join();
|
| +}
|
| +
|
| +TEST_F(TaskSchedulerSingleThreadTaskRunnerManagerJoinTest,
|
| + ConcurrentJoinExtraSkippedTask) {
|
| + WaitableEvent task_running(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + WaitableEvent task_blocking(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| +
|
| + {
|
| + auto task_runner = single_thread_task_runner_manager_
|
| + ->CreateSingleThreadTaskRunnerWithTraits(
|
| + TaskTraits().WithBaseSyncPrimitives());
|
| + EXPECT_TRUE(task_runner->PostTask(
|
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_running))));
|
| + EXPECT_TRUE(task_runner->PostTask(
|
| + FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&task_blocking))));
|
| + EXPECT_TRUE(task_runner->PostTask(FROM_HERE, Bind(&DoNothing)));
|
| + }
|
| +
|
| + task_running.Wait();
|
| + CallJoinFromDifferentThread join_from_different_thread(
|
| + single_thread_task_runner_manager_.get());
|
| + join_from_different_thread.Start();
|
| + join_from_different_thread.WaitForRunToStart();
|
| + task_blocking.Signal();
|
| + join_from_different_thread.Join();
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace base
|
|
|