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(); |
} |