123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- // 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 <functional>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/containers/queue.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/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/sequenced_task_runner_handle.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 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, 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.
- class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
- public:
- SimpleSingleThreadTaskRunner() = default;
- SimpleSingleThreadTaskRunner(const SimpleSingleThreadTaskRunner&) = delete;
- SimpleSingleThreadTaskRunner& operator=(const SimpleSingleThreadTaskRunner&) =
- delete;
- bool PostDelayedTask(const Location& from_here,
- OnceClosure task,
- base::TimeDelta delay) override {
- if (delay.is_positive())
- 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_;
- };
- // 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, TimeDelta timeout) 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 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.
- class RunLoopTestEnvironment {
- public:
- explicit RunLoopTestEnvironment(RunLoopTestType type) {
- switch (type) {
- case RunLoopTestType::kRealEnvironment: {
- task_environment_ = std::make_unique<test::TaskEnvironment>();
- 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::TaskEnvironment> task_environment_;
- std::unique_ptr<InjectableTestDelegate> test_delegate_;
- };
- class RunLoopTest : public testing::TestWithParam<RunLoopTestType> {
- public:
- RunLoopTest(const RunLoopTest&) = delete;
- RunLoopTest& operator=(const RunLoopTest&) = delete;
- protected:
- RunLoopTest() : test_environment_(GetParam()) {}
- RunLoopTestEnvironment test_environment_;
- RunLoop run_loop_;
- };
- } // namespace
- TEST_P(RunLoopTest, QuitWhenIdle) {
- int counter = 0;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&QuitWhenIdleTask, Unretained(&run_loop_),
- Unretained(&counter)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- MakeExpectedRunClosure(FROM_HERE));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE), Days(1));
- run_loop_.Run();
- EXPECT_EQ(1, counter);
- }
- TEST_P(RunLoopTest, QuitWhenIdleNestedLoop) {
- int counter = 0;
- 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,
- MakeExpectedRunClosure(FROM_HERE));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE), Days(1));
- run_loop_.Run();
- EXPECT_EQ(3, counter);
- }
- TEST_P(RunLoopTest, QuitWhenIdleClosure) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop_.QuitWhenIdleClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- MakeExpectedRunClosure(FROM_HERE));
- ThreadTaskRunnerHandle::Get()->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.
- ThreadTaskRunnerHandle::Get()->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();
- ThreadTaskRunnerHandle::Get()->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.
- ThreadTaskRunnerHandle::Get()->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();
- ThreadTaskRunnerHandle::Get()->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.
- ThreadTaskRunnerHandle::Get()->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.
- ThreadTaskRunnerHandle::Get()->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();
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- MakeExpectedRunClosure(FROM_HERE));
- other_sequence->PostTask(
- FROM_HERE,
- base::BindOnce([](RunLoop* run_loop) { run_loop->QuitWhenIdle(); },
- Unretained(&run_loop_)));
- ThreadTaskRunnerHandle::Get()->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();
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- MakeExpectedRunClosure(FROM_HERE));
- other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure());
- ThreadTaskRunnerHandle::Get()->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());
- 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();
- }
- TEST_P(RunLoopTest, CannotRunMoreThanOnce) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
- run_loop_.Run();
- EXPECT_DCHECK_DEATH({ run_loop_.Run(); });
- }
- TEST_P(RunLoopTest, CanRunUntilIdleMoreThanOnce) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
- run_loop_.RunUntilIdle();
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
- run_loop_.RunUntilIdle();
- run_loop_.RunUntilIdle();
- }
- TEST_P(RunLoopTest, CanRunUntilIdleThenRunIfNotQuit) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
- run_loop_.RunUntilIdle();
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
- run_loop_.Run();
- }
- TEST_P(RunLoopTest, CannotRunUntilIdleThenRunIfQuit) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure());
- run_loop_.RunUntilIdle();
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
- EXPECT_DCHECK_DEATH({ run_loop_.Run(); });
- }
- TEST_P(RunLoopTest, CannotRunAgainIfQuitWhenIdle) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- run_loop_.QuitWhenIdleClosure());
- run_loop_.RunUntilIdle();
- EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle(); });
- }
- namespace {
- class MockNestingObserver : public RunLoop::NestingObserver {
- public:
- MockNestingObserver() = default;
- MockNestingObserver(const MockNestingObserver&) = delete;
- MockNestingObserver& operator=(const MockNestingObserver&) = delete;
- // RunLoop::NestingObserver:
- MOCK_METHOD0(OnBeginNestedRunLoop, void());
- MOCK_METHOD0(OnExitNestedRunLoop, void());
- };
- class MockTask {
- public:
- MockTask() = default;
- MockTask(const MockTask&) = delete;
- MockTask& operator=(const MockTask&) = 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);
- const RepeatingClosure run_nested_loop = BindRepeating([]() {
- 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, 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 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();
- },
- 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
|