mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 00:06:09 +03:00
314 lines
9.9 KiB
C++
314 lines
9.9 KiB
C++
// 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/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::Client::ShouldQuitWhenIdle() const {
|
|
DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
|
|
DCHECK(outer_->bound_);
|
|
return outer_->active_run_loops_.top()->quit_when_idle_received_;
|
|
}
|
|
|
|
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) {}
|
|
|
|
// static
|
|
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(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_);
|
|
|
|
DCHECK(IsNestingAllowedOnCurrentThread() ||
|
|
type_ != Type::kNestableTasksAllowed);
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
#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) {
|
|
CHECK(delegate_->allow_nesting_);
|
|
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();
|
|
|
|
// Execute deferred Quit, if any:
|
|
if (previous_run_loop && previous_run_loop->quit_called_)
|
|
delegate_->Quit();
|
|
}
|
|
|
|
} // namespace base
|