blob: 6de1707b8f6885e9b2eb2e9be5882311f2752de5 [file] [log] [blame]
// 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/lazy_instance.h"
#include "base/threading/thread_local.h"
#include "base/tracked_objects.h"
#include "build/build_config.h"
namespace base {
namespace {
LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
LAZY_INSTANCE_INITIALIZER;
} // 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);
}
RunLoop* RunLoop::Delegate::Client::GetTopMostRunLoop() const {
DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
DCHECK(outer_->bound_);
return outer_->active_run_loops_.empty() ? nullptr
: outer_->active_run_loops_.top();
}
bool RunLoop::Delegate::Client::IsNested() const {
DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
DCHECK(outer_->bound_);
return outer_->active_run_loops_.size() > 1;
}
RunLoop::Delegate::Client::Client(Delegate* outer) : outer_(outer) {}
RunLoop::Delegate::Client* 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());
tls_delegate.Get().Set(delegate);
delegate->bound_ = true;
return &delegate->client_interface_;
}
RunLoop::RunLoop() : delegate_(tls_delegate.Get().Get()), weak_factory_(this) {
// A RunLoop::Delegate must be bound to this thread prior to using RunLoop.
DCHECK(delegate_);
}
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_);
// Use task stopwatch to exclude the loop run time from the current task, if
// any.
tracked_objects::TaskStopwatch stopwatch;
stopwatch.Start();
delegate_->Run();
stopwatch.Stop();
// 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() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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_);
return 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_);
return 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);
CHECK(delegate->allow_nesting_);
delegate->nesting_observers_.AddObserver(observer);
}
// static
void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
Delegate* delegate = tls_delegate.Get().Get();
DCHECK(delegate);
CHECK(delegate->allow_nesting_);
delegate->nesting_observers_.RemoveObserver(observer);
}
// static
bool RunLoop::IsNestingAllowedOnCurrentThread() {
return tls_delegate.Get().Get()->allow_nesting_;
}
// static
void RunLoop::DisallowNestingOnCurrentThread() {
tls_delegate.Get().Get()->allow_nesting_ = false;
}
bool RunLoop::BeforeRun() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!run_called_);
run_called_ = true;
// 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) {
CHECK(delegate_->allow_nesting_);
for (auto& observer : delegate_->nesting_observers_)
observer.OnBeginNestedRunLoop();
}
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();
// Execute deferred QuitNow, if any:
if (previous_run_loop && previous_run_loop->quit_called_)
delegate_->Quit();
}
} // namespace base