| // Copyright 2016 The Chromium Authors |
| // 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<functional> |
| #include<memory> |
| #include<utility> |
| |
| #include"base/containers/queue.h" |
| #include"base/functional/bind.h" |
| #include"base/functional/callback_helpers.h" |
| #include"base/location.h" |
| #include"base/memory/ptr_util.h" |
| #include"base/memory/ref_counted.h" |
| #include"base/synchronization/lock.h" |
| #include"base/synchronization/waitable_event.h" |
| #include"base/task/sequenced_task_runner.h" |
| #include"base/task/single_thread_task_runner.h" |
| #include"base/test/bind.h" |
| #include"base/test/gtest_util.h" |
| #include"base/test/scoped_run_loop_timeout.h" |
| #include"base/test/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"build/build_config.h" |
| #include"testing/gmock/include/gmock/gmock.h" |
| #include"testing/gtest/include/gtest/gtest.h" |
| |
| namespace base{ |
| |
| namespace{ |
| |
| voidQuitWhenIdleTask(RunLoop* run_loop,int* counter){ |
| run_loop->QuitWhenIdle(); |
| ++(*counter); |
| } |
| |
| voidRunNestedLoopTask(int* counter){ |
| RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); |
| |
| // This task should quit |nested_run_loop| but not the main RunLoop. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&nested_run_loop), |
| Unretained(counter))); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(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. |
| classSimpleSingleThreadTaskRunner:publicSingleThreadTaskRunner{ |
| public: |
| SimpleSingleThreadTaskRunner()=default; |
| SimpleSingleThreadTaskRunner(constSimpleSingleThreadTaskRunner&)=delete; |
| SimpleSingleThreadTaskRunner&operator=(constSimpleSingleThreadTaskRunner&)= |
| delete; |
| |
| boolPostDelayedTask(constLocation& from_here, |
| OnceClosure task, |
| base::TimeDelta delay) override{ |
| if(delay.is_positive()){ |
| returnfalse; |
| } |
| AutoLock auto_lock(tasks_lock_); |
| pending_tasks_.push(std::move(task)); |
| returntrue; |
| } |
| |
| boolPostNonNestableDelayedTask(constLocation& from_here, |
| OnceClosure task, |
| base::TimeDelta delay) override{ |
| returnPostDelayedTask(from_here, std::move(task), delay); |
| } |
| |
| boolRunsTasksInCurrentSequence()const override{ |
| return origin_thread_checker_.CalledOnValidThread(); |
| } |
| |
| boolProcessSingleTask(){ |
| OnceClosure task; |
| { |
| AutoLock auto_lock(tasks_lock_); |
| if(pending_tasks_.empty()){ |
| returnfalse; |
| } |
| 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(); |
| returntrue; |
| } |
| |
| 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_; |
| }; |
| |
| // 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). |
| classInjectableTestDelegate:publicRunLoop::Delegate{ |
| public: |
| voidInjectClosureOnDelegate(OnceClosure closure){ |
| AutoLock auto_lock(closure_lock_); |
| closure_= std::move(closure); |
| } |
| |
| boolRunInjectedClosure(){ |
| AutoLock auto_lock(closure_lock_); |
| if(closure_.is_null()){ |
| returnfalse; |
| } |
| std::move(closure_).Run(); |
| returntrue; |
| } |
| |
| 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. |
| classTestBoundDelegate final:publicInjectableTestDelegate{ |
| public: |
| TestBoundDelegate()=default; |
| |
| // Makes this TestBoundDelegate become the RunLoop::Delegate and |
| // SingleThreadTaskRunner::CurrentDefaultHandle for this thread. |
| voidBindToCurrentThread(){ |
| thread_task_runner_handle_= |
| std::make_unique<SingleThreadTaskRunner::CurrentDefaultHandle>( |
| simple_task_runner_); |
| RunLoop::RegisterDelegateForCurrentThread(this); |
| } |
| |
| private: |
| voidRun(bool application_tasks_allowed,TimeDelta timeout) override{ |
| if(nested_run_allowing_tasks_incoming_){ |
| EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread()); |
| EXPECT_TRUE(application_tasks_allowed); |
| }elseif(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; |
| } |
| |
| voidQuit() override{ should_quit_=true;} |
| |
| voidEnsureWorkScheduled() 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<SingleThreadTaskRunner::CurrentDefaultHandle> |
| thread_task_runner_handle_; |
| |
| bool should_quit_=false; |
| }; |
| |
| enumclassRunLoopTestType{ |
| // Runs all RunLoopTests under a TaskEnvironment 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. |
| classRunLoopTestEnvironment{ |
| public: |
| explicitRunLoopTestEnvironment(RunLoopTestType type){ |
| switch(type){ |
| caseRunLoopTestType::kRealEnvironment:{ |
| task_environment_= std::make_unique<test::TaskEnvironment>(); |
| break; |
| } |
| caseRunLoopTestType::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::TaskEnvironment> task_environment_; |
| std::unique_ptr<InjectableTestDelegate> test_delegate_; |
| }; |
| |
| classRunLoopTest:public testing::TestWithParam<RunLoopTestType>{ |
| public: |
| RunLoopTest(constRunLoopTest&)=delete; |
| RunLoopTest&operator=(constRunLoopTest&)=delete; |
| |
| protected: |
| RunLoopTest(): test_environment_(GetParam()){} |
| |
| RunLoopTestEnvironment test_environment_; |
| RunLoop run_loop_; |
| }; |
| |
| }// namespace |
| |
| TEST_P(RunLoopTest,QuitWhenIdle){ |
| int counter=0; |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&run_loop_), |
| Unretained(&counter))); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1)); |
| |
| run_loop_.Run(); |
| EXPECT_EQ(1, counter); |
| } |
| |
| TEST_P(RunLoopTest,QuitWhenIdleNestedLoop){ |
| int counter=0; |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,BindOnce(&RunNestedLoopTask,Unretained(&counter))); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&run_loop_), |
| Unretained(&counter))); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1)); |
| |
| run_loop_.Run(); |
| EXPECT_EQ(3, counter); |
| } |
| |
| TEST_P(RunLoopTest,QuitWhenIdleClosure){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitWhenIdleClosure()); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1)); |
| |
| run_loop_.Run(); |
| } |
| |
| // Verify that the QuitWhenIdleClosure() can run after the RunLoop has been |
| // deleted. It should have no effect. |
| TEST_P(RunLoopTest,QuitWhenIdleClosureAfterRunLoopScope){ |
| RepeatingClosure 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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| 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(); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE)); |
| |
| run_loop_.Run(); |
| } |
| |
| // 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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| 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(); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE)); |
| |
| run_loop_.Run(); |
| } |
| |
| // 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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure()); |
| |
| run_loop_.Run(); |
| } |
| |
| // 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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure()); |
| |
| run_loop_.Run(); |
| } |
| |
| // 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(); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| other_sequence->PostTask( |
| FROM_HERE, |
| base::BindOnce([](RunLoop* run_loop){ run_loop->QuitWhenIdle();}, |
| Unretained(&run_loop_))); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| run_loop_.Run(); |
| |
| // Regardless of the outcome of the race this thread shouldn't have been idle |
| // until both tasks posted to this sequence have run. |
| } |
| |
| // 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(); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure()); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,MakeExpectedRunClosure(FROM_HERE)); |
| |
| run_loop_.Run(); |
| |
| // Regardless of the outcome of the race this thread shouldn't have been idle |
| // until the both tasks posted to this sequence have run. |
| } |
| |
| TEST_P(RunLoopTest,IsRunningOnCurrentThread){ |
| EXPECT_FALSE(RunLoop::IsRunningOnCurrentThread()); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| BindOnce([]{ EXPECT_TRUE(RunLoop::IsRunningOnCurrentThread());})); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| run_loop_.Run(); |
| } |
| |
| TEST_P(RunLoopTest,IsNestedOnCurrentThread){ |
| EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE,BindOnce([]{ |
| EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); |
| |
| RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| BindOnce([]{ EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());})); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, nested_run_loop.QuitClosure()); |
| |
| EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); |
| nested_run_loop.Run(); |
| EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); |
| })); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| run_loop_.Run(); |
| } |
| |
| TEST_P(RunLoopTest,CannotRunMoreThanOnce){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| run_loop_.Run(); |
| EXPECT_DCHECK_DEATH({ run_loop_.Run();}); |
| } |
| |
| TEST_P(RunLoopTest,CanRunUntilIdleMoreThanOnce){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing()); |
| run_loop_.RunUntilIdle(); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing()); |
| run_loop_.RunUntilIdle(); |
| run_loop_.RunUntilIdle(); |
| } |
| |
| TEST_P(RunLoopTest,CanRunUntilIdleThenRunIfNotQuit){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing()); |
| run_loop_.RunUntilIdle(); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| run_loop_.Run(); |
| } |
| |
| TEST_P(RunLoopTest,CannotRunUntilIdleThenRunIfQuit){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitClosure()); |
| run_loop_.RunUntilIdle(); |
| |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing()); |
| EXPECT_DCHECK_DEATH({ run_loop_.Run();}); |
| } |
| |
| TEST_P(RunLoopTest,CannotRunAgainIfQuitWhenIdle){ |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, run_loop_.QuitWhenIdleClosure()); |
| run_loop_.RunUntilIdle(); |
| |
| EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle();}); |
| } |
| |
| namespace{ |
| |
| classMockNestingObserver:publicRunLoop::NestingObserver{ |
| public: |
| MockNestingObserver()=default; |
| MockNestingObserver(constMockNestingObserver&)=delete; |
| MockNestingObserver&operator=(constMockNestingObserver&)=delete; |
| |
| // RunLoop::NestingObserver: |
| MOCK_METHOD(void,OnBeginNestedRunLoop,()); |
| MOCK_METHOD(void,OnExitNestedRunLoop,()); |
| }; |
| |
| classMockTask{ |
| public: |
| MockTask()=default; |
| MockTask(constMockTask&)=delete; |
| MockTask&operator=(constMockTask&)=delete; |
| |
| MOCK_METHOD0(Task,void()); |
| }; |
| |
| }// 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); |
| |
| constRepeatingClosure run_nested_loop=BindRepeating([]{ |
| RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); |
| SingleThreadTaskRunner::GetCurrentDefault()->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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, |
| run_nested_loop); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a))); |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, |
| run_nested_loop); |
| SingleThreadTaskRunner::GetCurrentDefault()->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,DisallowRunning){ |
| ScopedDisallowRunningRunLoop disallow_running; |
| EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle();}); |
| } |
| |
| TEST_P(RunLoopTest,ExpiredDisallowRunning){ |
| {ScopedDisallowRunningRunLoop disallow_running;} |
| // Running should be fine after |disallow_running| goes out of scope. |
| run_loop_.RunUntilIdle(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Real, |
| RunLoopTest, |
| testing::Values(RunLoopTestType::kRealEnvironment)); |
| INSTANTIATE_TEST_SUITE_P(Mock, |
| RunLoopTest, |
| testing::Values(RunLoopTestType::kTestDelegate)); |
| |
| TEST(RunLoopDeathTest,MustRegisterBeforeInstantiating){ |
| TestBoundDelegate unbound_test_delegate_; |
| // RunLoop::RunLoop() should CHECK fetching the |
| // SingleThreadTaskRunner::CurrentDefaultHandle. |
| 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. |
| SingleThreadTaskRunner::GetCurrentDefault()->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. |
| SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| BindOnce( |
| [](constbool& nested_run_loop_ended, |
| OnceClosure continuation_callback){ |
| EXPECT_TRUE(nested_run_loop_ended); |
| EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread()); |
| std::move(continuation_callback).Run(); |
| }, |
| std::cref(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 |