|  | // Copyright 2016 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/run_loop.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/containers/queue.h" | 
|  | #include "base/location.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/test/scoped_task_environment.h" | 
|  | #include "base/test/test_timeouts.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/thread.h" | 
|  | #include "base/threading/thread_checker_impl.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "build/build_config.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void QuitWhenIdleTask(RunLoop* run_loop, int* counter) { | 
|  | run_loop->QuitWhenIdle(); | 
|  | ++(*counter); | 
|  | } | 
|  |  | 
|  | void ShouldRunTask(int* counter) { | 
|  | ++(*counter); | 
|  | } | 
|  |  | 
|  | void ShouldNotRunTask() { | 
|  | ADD_FAILURE() << "Ran a task that shouldn't run."; | 
|  | } | 
|  |  | 
|  | void RunNestedLoopTask(int* counter) { | 
|  | RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); | 
|  |  | 
|  | // This task should quit |nested_run_loop| but not the main RunLoop. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&nested_run_loop), | 
|  | Unretained(counter))); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 
|  | FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1)); | 
|  |  | 
|  | nested_run_loop.Run(); | 
|  |  | 
|  | ++(*counter); | 
|  | } | 
|  |  | 
|  | // A simple SingleThreadTaskRunner that just queues undelayed tasks (and ignores | 
|  | // delayed tasks). Tasks can then be processed one by one by ProcessTask() which | 
|  | // will return true if it processed a task and false otherwise. | 
|  | class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner { | 
|  | public: | 
|  | SimpleSingleThreadTaskRunner() = default; | 
|  |  | 
|  | bool PostDelayedTask(const Location& from_here, | 
|  | OnceClosure task, | 
|  | base::TimeDelta delay) override { | 
|  | if (delay > base::TimeDelta()) | 
|  | return false; | 
|  | AutoLock auto_lock(tasks_lock_); | 
|  | pending_tasks_.push(std::move(task)); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool PostNonNestableDelayedTask(const Location& from_here, | 
|  | OnceClosure task, | 
|  | base::TimeDelta delay) override { | 
|  | return PostDelayedTask(from_here, std::move(task), delay); | 
|  | } | 
|  |  | 
|  | bool RunsTasksInCurrentSequence() const override { | 
|  | return origin_thread_checker_.CalledOnValidThread(); | 
|  | } | 
|  |  | 
|  | bool ProcessSingleTask() { | 
|  | OnceClosure task; | 
|  | { | 
|  | AutoLock auto_lock(tasks_lock_); | 
|  | if (pending_tasks_.empty()) | 
|  | return false; | 
|  | task = std::move(pending_tasks_.front()); | 
|  | pending_tasks_.pop(); | 
|  | } | 
|  | // It's important to Run() after pop() and outside the lock as |task| may | 
|  | // run a nested loop which will re-enter ProcessSingleTask(). | 
|  | std::move(task).Run(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ~SimpleSingleThreadTaskRunner() override = default; | 
|  |  | 
|  | Lock tasks_lock_; | 
|  | base::queue<OnceClosure> pending_tasks_; | 
|  |  | 
|  | // RunLoop relies on RunsTasksInCurrentSequence() signal. Use a | 
|  | // ThreadCheckerImpl to be able to reliably provide that signal even in | 
|  | // non-dcheck builds. | 
|  | ThreadCheckerImpl origin_thread_checker_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(SimpleSingleThreadTaskRunner); | 
|  | }; | 
|  |  | 
|  | // The basis of all TestDelegates, allows safely injecting a OnceClosure to be | 
|  | // run in the next idle phase of this delegate's Run() implementation. This can | 
|  | // be used to have code run on a thread that is otherwise livelocked in an idle | 
|  | // phase (sometimes a simple PostTask() won't do it -- e.g. when processing | 
|  | // application tasks is disallowed). | 
|  | class InjectableTestDelegate : public RunLoop::Delegate { | 
|  | public: | 
|  | void InjectClosureOnDelegate(OnceClosure closure) { | 
|  | AutoLock auto_lock(closure_lock_); | 
|  | closure_ = std::move(closure); | 
|  | } | 
|  |  | 
|  | bool RunInjectedClosure() { | 
|  | AutoLock auto_lock(closure_lock_); | 
|  | if (closure_.is_null()) | 
|  | return false; | 
|  | std::move(closure_).Run(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Lock closure_lock_; | 
|  | OnceClosure closure_; | 
|  | }; | 
|  |  | 
|  | // A simple test RunLoop::Delegate to exercise Runloop logic independent of any | 
|  | // other base constructs. BindToCurrentThread() must be called before this | 
|  | // TestBoundDelegate is operational. | 
|  | class TestBoundDelegate final : public InjectableTestDelegate { | 
|  | public: | 
|  | TestBoundDelegate() = default; | 
|  |  | 
|  | // Makes this TestBoundDelegate become the RunLoop::Delegate and | 
|  | // ThreadTaskRunnerHandle for this thread. | 
|  | void BindToCurrentThread() { | 
|  | thread_task_runner_handle_ = | 
|  | std::make_unique<ThreadTaskRunnerHandle>(simple_task_runner_); | 
|  | RunLoop::RegisterDelegateForCurrentThread(this); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void Run(bool application_tasks_allowed) override { | 
|  | if (nested_run_allowing_tasks_incoming_) { | 
|  | EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread()); | 
|  | EXPECT_TRUE(application_tasks_allowed); | 
|  | } else if (RunLoop::IsNestedOnCurrentThread()) { | 
|  | EXPECT_FALSE(application_tasks_allowed); | 
|  | } | 
|  | nested_run_allowing_tasks_incoming_ = false; | 
|  |  | 
|  | while (!should_quit_) { | 
|  | if (application_tasks_allowed && simple_task_runner_->ProcessSingleTask()) | 
|  | continue; | 
|  |  | 
|  | if (ShouldQuitWhenIdle()) | 
|  | break; | 
|  |  | 
|  | if (RunInjectedClosure()) | 
|  | continue; | 
|  |  | 
|  | PlatformThread::YieldCurrentThread(); | 
|  | } | 
|  | should_quit_ = false; | 
|  | } | 
|  |  | 
|  | void Quit() override { should_quit_ = true; } | 
|  |  | 
|  | void EnsureWorkScheduled() override { | 
|  | nested_run_allowing_tasks_incoming_ = true; | 
|  | } | 
|  |  | 
|  | // True if the next invocation of Run() is expected to be from a | 
|  | // kNestableTasksAllowed RunLoop. | 
|  | bool nested_run_allowing_tasks_incoming_ = false; | 
|  |  | 
|  | scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ = | 
|  | MakeRefCounted<SimpleSingleThreadTaskRunner>(); | 
|  |  | 
|  | std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_; | 
|  |  | 
|  | bool should_quit_ = false; | 
|  | }; | 
|  |  | 
|  | enum class RunLoopTestType { | 
|  | // Runs all RunLoopTests under a ScopedTaskEnvironment to make sure real world | 
|  | // scenarios work. | 
|  | kRealEnvironment, | 
|  |  | 
|  | // Runs all RunLoopTests under a test RunLoop::Delegate to make sure the | 
|  | // delegate interface fully works standalone. | 
|  | kTestDelegate, | 
|  | }; | 
|  |  | 
|  | // The task environment for the RunLoopTest of a given type. A separate class | 
|  | // so it can be instantiated on the stack in the RunLoopTest fixture. | 
|  | class RunLoopTestEnvironment { | 
|  | public: | 
|  | RunLoopTestEnvironment(RunLoopTestType type) { | 
|  | switch (type) { | 
|  | case RunLoopTestType::kRealEnvironment: { | 
|  | task_environment_ = std::make_unique<test::ScopedTaskEnvironment>(); | 
|  | break; | 
|  | } | 
|  | case RunLoopTestType::kTestDelegate: { | 
|  | auto test_delegate = std::make_unique<TestBoundDelegate>(); | 
|  | test_delegate->BindToCurrentThread(); | 
|  | test_delegate_ = std::move(test_delegate); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Instantiates one or the other based on the RunLoopTestType. | 
|  | std::unique_ptr<test::ScopedTaskEnvironment> task_environment_; | 
|  | std::unique_ptr<InjectableTestDelegate> test_delegate_; | 
|  | }; | 
|  |  | 
|  | class RunLoopTest : public testing::TestWithParam<RunLoopTestType> { | 
|  | protected: | 
|  | RunLoopTest() : test_environment_(GetParam()) {} | 
|  |  | 
|  | RunLoopTestEnvironment test_environment_; | 
|  | RunLoop run_loop_; | 
|  | int counter_ = 0; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(RunLoopTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_P(RunLoopTest, QuitWhenIdle) { | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_), | 
|  | Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 
|  | FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1)); | 
|  |  | 
|  | run_loop_.Run(); | 
|  | EXPECT_EQ(2, counter_); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, QuitWhenIdleNestedLoop) { | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&RunNestedLoopTask, Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_), | 
|  | Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 
|  | FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1)); | 
|  |  | 
|  | run_loop_.Run(); | 
|  | EXPECT_EQ(4, counter_); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, QuitWhenIdleClosure) { | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
|  | run_loop_.QuitWhenIdleClosure()); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  | ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 
|  | FROM_HERE, BindOnce(&ShouldNotRunTask), TimeDelta::FromDays(1)); | 
|  |  | 
|  | run_loop_.Run(); | 
|  | EXPECT_EQ(1, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that the QuitWhenIdleClosure() can run after the RunLoop has been | 
|  | // deleted. It should have no effect. | 
|  | TEST_P(RunLoopTest, QuitWhenIdleClosureAfterRunLoopScope) { | 
|  | Closure quit_when_idle_closure; | 
|  | { | 
|  | RunLoop run_loop; | 
|  | quit_when_idle_closure = run_loop.QuitWhenIdleClosure(); | 
|  | run_loop.RunUntilIdle(); | 
|  | } | 
|  | quit_when_idle_closure.Run(); | 
|  | } | 
|  |  | 
|  | // Verify that Quit can be executed from another sequence. | 
|  | TEST_P(RunLoopTest, QuitFromOtherSequence) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | // Always expected to run before asynchronous Quit() kicks in. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED); | 
|  | other_sequence->PostTask( | 
|  | FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); }, | 
|  | Unretained(&run_loop_))); | 
|  | other_sequence->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit))); | 
|  |  | 
|  | // Anything that's posted after the Quit closure was posted back to this | 
|  | // sequence shouldn't get a chance to run. | 
|  | loop_was_quit.Wait(); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
|  | base::BindOnce(&ShouldNotRunTask)); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | EXPECT_EQ(1, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that QuitClosure can be executed from another sequence. | 
|  | TEST_P(RunLoopTest, QuitFromOtherSequenceWithClosure) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | // Always expected to run before asynchronous Quit() kicks in. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED); | 
|  | other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure()); | 
|  | other_sequence->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit))); | 
|  |  | 
|  | // Anything that's posted after the Quit closure was posted back to this | 
|  | // sequence shouldn't get a chance to run. | 
|  | loop_was_quit.Wait(); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
|  | base::BindOnce(&ShouldNotRunTask)); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | EXPECT_EQ(1, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that Quit can be executed from another sequence even when the | 
|  | // Quit is racing with Run() -- i.e. forgo the WaitableEvent used above. | 
|  | TEST_P(RunLoopTest, QuitFromOtherSequenceRacy) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | // Always expected to run before asynchronous Quit() kicks in. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | other_sequence->PostTask( | 
|  | FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); }, | 
|  | Unretained(&run_loop_))); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | EXPECT_EQ(1, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that QuitClosure can be executed from another sequence even when the | 
|  | // Quit is racing with Run() -- i.e. forgo the WaitableEvent used above. | 
|  | TEST_P(RunLoopTest, QuitFromOtherSequenceRacyWithClosure) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | // Always expected to run before asynchronous Quit() kicks in. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure()); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | EXPECT_EQ(1, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that QuitWhenIdle can be executed from another sequence. | 
|  | TEST_P(RunLoopTest, QuitWhenIdleFromOtherSequence) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | other_sequence->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce([](RunLoop* run_loop) { run_loop->QuitWhenIdle(); }, | 
|  | Unretained(&run_loop_))); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | // Regardless of the outcome of the race this thread shouldn't have been idle | 
|  | // until the counter was ticked twice. | 
|  | EXPECT_EQ(2, counter_); | 
|  | } | 
|  |  | 
|  | // Verify that QuitWhenIdleClosure can be executed from another sequence. | 
|  | TEST_P(RunLoopTest, QuitWhenIdleFromOtherSequenceWithClosure) { | 
|  | Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  | scoped_refptr<SequencedTaskRunner> other_sequence = | 
|  | other_thread.task_runner(); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure()); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_))); | 
|  |  | 
|  | run_loop_.Run(); | 
|  |  | 
|  | // Regardless of the outcome of the race this thread shouldn't have been idle | 
|  | // until the counter was ticked twice. | 
|  | EXPECT_EQ(2, counter_); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, IsRunningOnCurrentThread) { | 
|  | EXPECT_FALSE(RunLoop::IsRunningOnCurrentThread()); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce([]() { EXPECT_TRUE(RunLoop::IsRunningOnCurrentThread()); })); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure()); | 
|  | run_loop_.Run(); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, IsNestedOnCurrentThread) { | 
|  | EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce([]() { | 
|  | EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); | 
|  |  | 
|  | RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce([]() { | 
|  | EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread()); | 
|  | })); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
|  | nested_run_loop.QuitClosure()); | 
|  |  | 
|  | EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); | 
|  | nested_run_loop.Run(); | 
|  | EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); | 
|  | })); | 
|  |  | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure()); | 
|  | run_loop_.Run(); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class MockNestingObserver : public RunLoop::NestingObserver { | 
|  | public: | 
|  | MockNestingObserver() = default; | 
|  |  | 
|  | // RunLoop::NestingObserver: | 
|  | MOCK_METHOD0(OnBeginNestedRunLoop, void()); | 
|  | MOCK_METHOD0(OnExitNestedRunLoop, void()); | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(MockNestingObserver); | 
|  | }; | 
|  |  | 
|  | class MockTask { | 
|  | public: | 
|  | MockTask() = default; | 
|  | MOCK_METHOD0(Task, void()); | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(MockTask); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_P(RunLoopTest, NestingObservers) { | 
|  | testing::StrictMock<MockNestingObserver> nesting_observer; | 
|  | testing::StrictMock<MockTask> mock_task_a; | 
|  | testing::StrictMock<MockTask> mock_task_b; | 
|  |  | 
|  | RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer); | 
|  |  | 
|  | const RepeatingClosure run_nested_loop = Bind([]() { | 
|  | RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 
|  | nested_run_loop.QuitClosure()); | 
|  | nested_run_loop.Run(); | 
|  | }); | 
|  |  | 
|  | // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected | 
|  | // when beginning each nesting depth and OnExitNestedRunLoop() is expected | 
|  | // when exiting each nesting depth. Each one of these tasks is ahead of the | 
|  | // QuitClosures as those are only posted at the end of the queue when | 
|  | // |run_nested_loop| is executed. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a))); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop); | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b))); | 
|  |  | 
|  | { | 
|  | testing::InSequence in_sequence; | 
|  | EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop()); | 
|  | EXPECT_CALL(mock_task_a, Task()); | 
|  | EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop()); | 
|  | EXPECT_CALL(mock_task_b, Task()); | 
|  | EXPECT_CALL(nesting_observer, OnExitNestedRunLoop()).Times(2); | 
|  | } | 
|  | run_loop_.RunUntilIdle(); | 
|  |  | 
|  | RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, DisallowRunningForTesting) { | 
|  | RunLoop::ScopedDisallowRunningForTesting disallow_running; | 
|  | EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle(); }); | 
|  | } | 
|  |  | 
|  | TEST_P(RunLoopTest, ExpiredDisallowRunningForTesting) { | 
|  | { RunLoop::ScopedDisallowRunningForTesting disallow_running; } | 
|  | // Running should be fine after |disallow_running| goes out of scope. | 
|  | run_loop_.RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P(Real, | 
|  | RunLoopTest, | 
|  | testing::Values(RunLoopTestType::kRealEnvironment)); | 
|  | INSTANTIATE_TEST_CASE_P(Mock, | 
|  | RunLoopTest, | 
|  | testing::Values(RunLoopTestType::kTestDelegate)); | 
|  |  | 
|  | TEST(RunLoopDeathTest, MustRegisterBeforeInstantiating) { | 
|  | TestBoundDelegate unbound_test_delegate_; | 
|  | // RunLoop::RunLoop() should CHECK fetching the ThreadTaskRunnerHandle. | 
|  | EXPECT_DEATH_IF_SUPPORTED({ RunLoop(); }, ""); | 
|  | } | 
|  |  | 
|  | TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) { | 
|  | TestBoundDelegate test_delegate; | 
|  | test_delegate.BindToCurrentThread(); | 
|  |  | 
|  | base::Thread other_thread("test"); | 
|  | other_thread.Start(); | 
|  |  | 
|  | RunLoop main_loop; | 
|  | // A nested run loop which isn't kNestableTasksAllowed. | 
|  | RunLoop nested_run_loop(RunLoop::Type::kDefault); | 
|  |  | 
|  | bool nested_run_loop_ended = false; | 
|  |  | 
|  | // The first task on the main loop will result in a nested run loop. Since | 
|  | // it's not kNestableTasksAllowed, no further task should be processed until | 
|  | // it's quit. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce([](RunLoop* nested_run_loop) { nested_run_loop->Run(); }, | 
|  | Unretained(&nested_run_loop))); | 
|  |  | 
|  | // Post a task that will fail if it runs inside the nested run loop. | 
|  | ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, BindOnce( | 
|  | [](const bool& nested_run_loop_ended, | 
|  | OnceClosure continuation_callback) { | 
|  | EXPECT_TRUE(nested_run_loop_ended); | 
|  | EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); | 
|  | std::move(continuation_callback).Run(); | 
|  | }, | 
|  | ConstRef(nested_run_loop_ended), main_loop.QuitClosure())); | 
|  |  | 
|  | // Post a task flipping the boolean bit for extra verification right before | 
|  | // quitting |nested_run_loop|. | 
|  | other_thread.task_runner()->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | BindOnce( | 
|  | [](bool* nested_run_loop_ended) { | 
|  | EXPECT_FALSE(*nested_run_loop_ended); | 
|  | *nested_run_loop_ended = true; | 
|  | }, | 
|  | Unretained(&nested_run_loop_ended)), | 
|  | TestTimeouts::tiny_timeout()); | 
|  | // Post an async delayed task to exit the run loop when idle. This confirms | 
|  | // that (1) the test task only ran in the main loop after the nested loop | 
|  | // exited and (2) the nested run loop actually considers itself idle while | 
|  | // spinning. Note: The quit closure needs to be injected directly on the | 
|  | // delegate as invoking QuitWhenIdle() off-thread results in a thread bounce | 
|  | // which will not processed because of the very logic under test (nestable | 
|  | // tasks don't run in |nested_run_loop|). | 
|  | other_thread.task_runner()->PostDelayedTask( | 
|  | FROM_HERE, | 
|  | BindOnce( | 
|  | [](TestBoundDelegate* test_delegate, OnceClosure injected_closure) { | 
|  | test_delegate->InjectClosureOnDelegate(std::move(injected_closure)); | 
|  | }, | 
|  | Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()), | 
|  | TestTimeouts::tiny_timeout()); | 
|  |  | 
|  | main_loop.Run(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |