Chromium Code Reviews| Index: base/run_loop.cc |
| diff --git a/base/run_loop.cc b/base/run_loop.cc |
| index 4c19d3589fd7e6f893a7a02f91d2c5a2371720ec..da2990dac96d0b801508bff5dc3ddabc2d8643c2 100644 |
| --- a/base/run_loop.cc |
| +++ b/base/run_loop.cc |
| @@ -4,29 +4,83 @@ |
| #include "base/run_loop.h" |
| +#include <stack> |
| + |
| #include "base/bind.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/observer_list.h" |
| +#include "base/threading/thread_local_storage.h" |
| #include "base/tracked_objects.h" |
| #include "build/build_config.h" |
| namespace base { |
| +namespace { |
| + |
| +class ThreadLocalRunLoopState { |
| + public: |
| + ThreadLocalRunLoopState() |
| + : slot_(&ThreadLocalRunLoopState::OnTLSDestruction) {} |
| + |
| + ~ThreadLocalRunLoopState() = delete; |
| + |
| + std::stack<RunLoop*>& GetActiveRunLoops() { |
| + return GetOrCreateInternalState()->active_run_loops; |
| + } |
| + |
| + ObserverList<RunLoop::NestingObserver>& GetNestingObservers() { |
| + InternalState* state = GetOrCreateInternalState(); |
| + CHECK(state->allow_nesting); |
| + return state->nesting_observers; |
| + } |
| + |
| + bool IsNestingAllowed() { return GetOrCreateInternalState()->allow_nesting; } |
| + |
| + void DisallowNesting() { GetOrCreateInternalState()->allow_nesting = false; } |
| + |
| + private: |
| + struct InternalState { |
| + bool allow_nesting = true; |
| + std::stack<RunLoop*> active_run_loops; |
|
danakj
2017/04/20 14:57:32
stacks are deques by default which are terrible fo
gab
2017/04/20 16:22:02
Good point, done.
|
| + ObserverList<RunLoop::NestingObserver> nesting_observers; |
| + }; |
| + |
| + static void OnTLSDestruction(void* internal_state) { |
| + delete static_cast<InternalState*>(internal_state); |
| + } |
| + |
| + InternalState* GetOrCreateInternalState() { |
| + InternalState* state = static_cast<InternalState*>(slot_.Get()); |
| + if (!state) { |
| + state = new InternalState; |
| + slot_.Set(static_cast<void*>(state)); |
| + } |
| + return state; |
| + } |
| + |
| + ThreadLocalStorage::Slot slot_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ThreadLocalRunLoopState); |
| +}; |
| + |
| +LazyInstance<ThreadLocalRunLoopState>::Leaky tls_run_loop_state = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +} // namespace |
| + |
| RunLoop::RunLoop() |
| : loop_(MessageLoop::current()), |
| - previous_run_loop_(NULL), |
| - run_depth_(0), |
| - run_called_(false), |
| - quit_called_(false), |
| - running_(false), |
| - quit_when_idle_received_(false), |
| weak_factory_(this) { |
| DCHECK(loop_); |
| } |
| RunLoop::~RunLoop() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| void RunLoop::Run() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| if (!BeforeRun()) |
| return; |
| @@ -41,12 +95,15 @@ void RunLoop::Run() { |
| } |
| void RunLoop::RunUntilIdle() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| quit_when_idle_received_ = true; |
| Run(); |
| } |
| void RunLoop::Quit() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| quit_called_ = true; |
| if (running_ && loop_->run_loop_ == this) { |
| // This is the inner-most RunLoop, so quit now. |
| @@ -60,14 +117,48 @@ void RunLoop::QuitWhenIdle() { |
| } |
| base::Closure RunLoop::QuitClosure() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()); |
| } |
| base::Closure RunLoop::QuitWhenIdleClosure() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()); |
| } |
| +// static |
| +bool RunLoop::IsRunningOnCurrentThread() { |
| + return !tls_run_loop_state.Get().GetActiveRunLoops().empty(); |
| +} |
| + |
| +// static |
| +bool RunLoop::IsNestedOnCurrentThread() { |
| + return tls_run_loop_state.Get().GetActiveRunLoops().size() > 1; |
| +} |
| + |
| +// static |
| +void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) { |
| + tls_run_loop_state.Get().GetNestingObservers().AddObserver(observer); |
| +} |
| + |
| +// static |
| +void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) { |
| + tls_run_loop_state.Get().GetNestingObservers().RemoveObserver(observer); |
| +} |
| + |
| +// static |
| +bool RunLoop::IsNestingAllowedOnCurrentThread() { |
| + return tls_run_loop_state.Get().IsNestingAllowed(); |
| +} |
| + |
| +// static |
| +void RunLoop::DisallowNestingOnCurrentThread() { |
| + tls_run_loop_state.Get().DisallowNesting(); |
| +} |
| + |
| bool RunLoop::BeforeRun() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| DCHECK(!run_called_); |
| run_called_ = true; |
| @@ -75,26 +166,44 @@ bool RunLoop::BeforeRun() { |
| if (quit_called_) |
| return false; |
| - // Push RunLoop stack: |
| - previous_run_loop_ = loop_->run_loop_; |
| - run_depth_ = previous_run_loop_? previous_run_loop_->run_depth_ + 1 : 1; |
| + std::stack<RunLoop*>& active_run_loops = |
|
danakj
2017/04/20 14:57:32
nit: auto?
gab
2017/04/20 16:22:01
Done.
|
| + tls_run_loop_state.Get().GetActiveRunLoops(); |
| + active_run_loops.push(this); |
| + |
| + const bool is_nested = active_run_loops.size() > 1; |
| + |
| + // TODO(gab): Break the inter-dependency between MessageLoop and RunLoop |
| + // further. http://crbug.com/703346 |
| loop_->run_loop_ = this; |
| + loop_->is_nested_ = is_nested; |
| - if (run_depth_ > 1) |
| - loop_->NotifyBeginNestedLoop(); |
| + if (is_nested) { |
| + CHECK(tls_run_loop_state.Get().IsNestingAllowed()); |
| + for (auto& observer : tls_run_loop_state.Get().GetNestingObservers()) |
| + observer.OnBeginNestedMessageLoop(); |
| + } |
| running_ = true; |
| return true; |
| } |
| void RunLoop::AfterRun() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| running_ = false; |
| - // Pop RunLoop stack: |
| - loop_->run_loop_ = previous_run_loop_; |
| + std::stack<RunLoop*>& active_run_loops = |
| + tls_run_loop_state.Get().GetActiveRunLoops(); |
| + DCHECK_EQ(active_run_loops.top(), this); |
| + active_run_loops.pop(); |
| + |
| + RunLoop* underlying_run_loop = |
|
danakj
2017/04/20 14:57:32
nit: i liked previous better than underlying as a
gab
2017/04/20 16:22:01
Done.
|
| + active_run_loops.empty() ? nullptr : active_run_loops.top(); |
| + loop_->run_loop_ = underlying_run_loop; |
| + loop_->is_nested_ = active_run_loops.size() > 1; |
| // Execute deferred QuitNow, if any: |
| - if (previous_run_loop_ && previous_run_loop_->quit_called_) |
| + if (underlying_run_loop && underlying_run_loop->quit_called_) |
| loop_->QuitNow(); |
| } |