|  | // Copyright (c) 2012 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 "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/threading/thread_local.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "build/build_config.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | // Runs |closure| immediately if this is called on |task_runner|, otherwise | 
|  | // forwards |closure| to it. | 
|  | void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner, | 
|  | OnceClosure closure) { | 
|  | if (task_runner->RunsTasksInCurrentSequence()) { | 
|  | std::move(closure).Run(); | 
|  | return; | 
|  | } | 
|  | task_runner->PostTask(FROM_HERE, std::move(closure)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | RunLoop::Delegate::Delegate() { | 
|  | // The Delegate can be created on another thread. It is only bound in | 
|  | // RegisterDelegateForCurrentThread(). | 
|  | DETACH_FROM_THREAD(bound_thread_checker_); | 
|  | } | 
|  |  | 
|  | RunLoop::Delegate::~Delegate() { | 
|  | DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); | 
|  | // A RunLoop::Delegate may be destroyed before it is bound, if so it may still | 
|  | // be on its creation thread (e.g. a Thread that fails to start) and | 
|  | // shouldn't disrupt that thread's state. | 
|  | if (bound_) | 
|  | tls_delegate.Get().Set(nullptr); | 
|  | } | 
|  |  | 
|  | bool RunLoop::Delegate::ShouldQuitWhenIdle() { | 
|  | return active_run_loops_.top()->quit_when_idle_received_; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void RunLoop::RegisterDelegateForCurrentThread(Delegate* delegate) { | 
|  | // Bind |delegate| to this thread. | 
|  | DCHECK(!delegate->bound_); | 
|  | DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_); | 
|  |  | 
|  | // There can only be one RunLoop::Delegate per thread. | 
|  | DCHECK(!tls_delegate.Get().Get()) | 
|  | << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n" | 
|  | "Hint: You perhaps instantiated a second " | 
|  | "MessageLoop/ScopedTaskEnvironment on a thread that already had one?"; | 
|  | tls_delegate.Get().Set(delegate); | 
|  | delegate->bound_ = true; | 
|  | } | 
|  |  | 
|  | RunLoop::RunLoop(Type type) | 
|  | : delegate_(tls_delegate.Get().Get()), | 
|  | type_(type), | 
|  | origin_task_runner_(ThreadTaskRunnerHandle::Get()), | 
|  | weak_factory_(this) { | 
|  | DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior " | 
|  | "to using RunLoop."; | 
|  | DCHECK(origin_task_runner_); | 
|  | } | 
|  |  | 
|  | RunLoop::~RunLoop() { | 
|  | // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. | 
|  | // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | } | 
|  |  | 
|  | void RunLoop::Run() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | if (!BeforeRun()) | 
|  | return; | 
|  |  | 
|  | // It is okay to access this RunLoop from another sequence while Run() is | 
|  | // active as this RunLoop won't touch its state until after that returns (if | 
|  | // the RunLoop's state is accessed while processing Run(), it will be re-bound | 
|  | // to the accessing sequence for the remainder of that Run() -- accessing from | 
|  | // multiple sequences is still disallowed). | 
|  | DETACH_FROM_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | DCHECK_EQ(this, delegate_->active_run_loops_.top()); | 
|  | const bool application_tasks_allowed = | 
|  | delegate_->active_run_loops_.size() == 1U || | 
|  | type_ == Type::kNestableTasksAllowed; | 
|  | delegate_->Run(application_tasks_allowed); | 
|  |  | 
|  | // Rebind this RunLoop to the current thread after Run(). | 
|  | DETACH_FROM_SEQUENCE(sequence_checker_); | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | AfterRun(); | 
|  | } | 
|  |  | 
|  | void RunLoop::RunUntilIdle() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | quit_when_idle_received_ = true; | 
|  | Run(); | 
|  | } | 
|  |  | 
|  | void RunLoop::Quit() { | 
|  | // Thread-safe. | 
|  |  | 
|  | // This can only be hit if run_loop->Quit() is called directly (QuitClosure() | 
|  | // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on | 
|  | // |origin_task_runner_|). | 
|  | if (!origin_task_runner_->RunsTasksInCurrentSequence()) { | 
|  | origin_task_runner_->PostTask( | 
|  | FROM_HERE, base::BindOnce(&RunLoop::Quit, Unretained(this))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | quit_called_ = true; | 
|  | if (running_ && delegate_->active_run_loops_.top() == this) { | 
|  | // This is the inner-most RunLoop, so quit now. | 
|  | delegate_->Quit(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RunLoop::QuitWhenIdle() { | 
|  | // Thread-safe. | 
|  |  | 
|  | // This can only be hit if run_loop->QuitWhenIdle() is called directly | 
|  | // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only | 
|  | // deref its WeakPtr on |origin_task_runner_|). | 
|  | if (!origin_task_runner_->RunsTasksInCurrentSequence()) { | 
|  | origin_task_runner_->PostTask( | 
|  | FROM_HERE, base::BindOnce(&RunLoop::QuitWhenIdle, Unretained(this))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | quit_when_idle_received_ = true; | 
|  | } | 
|  |  | 
|  | base::Closure RunLoop::QuitClosure() { | 
|  | // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. | 
|  | // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | // Need to use ProxyToTaskRunner() as WeakPtrs vended from | 
|  | // |weak_factory_| may only be accessed on |origin_task_runner_|. | 
|  | // TODO(gab): It feels wrong that QuitClosure() is bound to a WeakPtr. | 
|  | return base::Bind(&ProxyToTaskRunner, origin_task_runner_, | 
|  | base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | base::Closure RunLoop::QuitWhenIdleClosure() { | 
|  | // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. | 
|  | // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | // Need to use ProxyToTaskRunner() as WeakPtrs vended from | 
|  | // |weak_factory_| may only be accessed on |origin_task_runner_|. | 
|  | // TODO(gab): It feels wrong that QuitWhenIdleClosure() is bound to a WeakPtr. | 
|  | return base::Bind( | 
|  | &ProxyToTaskRunner, origin_task_runner_, | 
|  | base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool RunLoop::IsRunningOnCurrentThread() { | 
|  | Delegate* delegate = tls_delegate.Get().Get(); | 
|  | return delegate && !delegate->active_run_loops_.empty(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool RunLoop::IsNestedOnCurrentThread() { | 
|  | Delegate* delegate = tls_delegate.Get().Get(); | 
|  | return delegate && delegate->active_run_loops_.size() > 1; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) { | 
|  | Delegate* delegate = tls_delegate.Get().Get(); | 
|  | DCHECK(delegate); | 
|  | delegate->nesting_observers_.AddObserver(observer); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) { | 
|  | Delegate* delegate = tls_delegate.Get().Get(); | 
|  | DCHECK(delegate); | 
|  | delegate->nesting_observers_.RemoveObserver(observer); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void RunLoop::QuitCurrentDeprecated() { | 
|  | DCHECK(IsRunningOnCurrentThread()); | 
|  | tls_delegate.Get().Get()->active_run_loops_.top()->Quit(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void RunLoop::QuitCurrentWhenIdleDeprecated() { | 
|  | DCHECK(IsRunningOnCurrentThread()); | 
|  | tls_delegate.Get().Get()->active_run_loops_.top()->QuitWhenIdle(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | Closure RunLoop::QuitCurrentWhenIdleClosureDeprecated() { | 
|  | return Bind(&RunLoop::QuitCurrentWhenIdleDeprecated); | 
|  | } | 
|  |  | 
|  | #if DCHECK_IS_ON() | 
|  | RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting() | 
|  | : current_delegate_(tls_delegate.Get().Get()), | 
|  | previous_run_allowance_( | 
|  | current_delegate_ ? current_delegate_->allow_running_for_testing_ | 
|  | : false) { | 
|  | if (current_delegate_) | 
|  | current_delegate_->allow_running_for_testing_ = false; | 
|  | } | 
|  |  | 
|  | RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() { | 
|  | DCHECK_EQ(current_delegate_, tls_delegate.Get().Get()); | 
|  | if (current_delegate_) | 
|  | current_delegate_->allow_running_for_testing_ = previous_run_allowance_; | 
|  | } | 
|  | #else   // DCHECK_IS_ON() | 
|  | // Defined out of line so that the compiler doesn't inline these and realize | 
|  | // the scope has no effect and then throws an "unused variable" warning in | 
|  | // non-dcheck builds. | 
|  | RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting() = | 
|  | default; | 
|  | RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() = | 
|  | default; | 
|  | #endif  // DCHECK_IS_ON() | 
|  |  | 
|  | bool RunLoop::BeforeRun() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | #if DCHECK_IS_ON() | 
|  | DCHECK(delegate_->allow_running_for_testing_) | 
|  | << "RunLoop::Run() isn't allowed in the scope of a " | 
|  | "ScopedDisallowRunningForTesting. Hint: if mixing " | 
|  | "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's " | 
|  | "API instead of RunLoop to drive individual task runners."; | 
|  | DCHECK(!run_called_); | 
|  | run_called_ = true; | 
|  | #endif  // DCHECK_IS_ON() | 
|  |  | 
|  | // Allow Quit to be called before Run. | 
|  | if (quit_called_) | 
|  | return false; | 
|  |  | 
|  | auto& active_run_loops_ = delegate_->active_run_loops_; | 
|  | active_run_loops_.push(this); | 
|  |  | 
|  | const bool is_nested = active_run_loops_.size() > 1; | 
|  |  | 
|  | if (is_nested) { | 
|  | for (auto& observer : delegate_->nesting_observers_) | 
|  | observer.OnBeginNestedRunLoop(); | 
|  | if (type_ == Type::kNestableTasksAllowed) | 
|  | delegate_->EnsureWorkScheduled(); | 
|  | } | 
|  |  | 
|  | running_ = true; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void RunLoop::AfterRun() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | running_ = false; | 
|  |  | 
|  | auto& active_run_loops_ = delegate_->active_run_loops_; | 
|  | DCHECK_EQ(active_run_loops_.top(), this); | 
|  | active_run_loops_.pop(); | 
|  |  | 
|  | RunLoop* previous_run_loop = | 
|  | active_run_loops_.empty() ? nullptr : active_run_loops_.top(); | 
|  |  | 
|  | if (previous_run_loop) { | 
|  | for (auto& observer : delegate_->nesting_observers_) | 
|  | observer.OnExitNestedRunLoop(); | 
|  | } | 
|  |  | 
|  | // Execute deferred Quit, if any: | 
|  | if (previous_run_loop && previous_run_loop->quit_called_) | 
|  | delegate_->Quit(); | 
|  | } | 
|  |  | 
|  | }  // namespace base |