Index: base/run_loop.cc |
diff --git a/base/run_loop.cc b/base/run_loop.cc |
index 4c19d3589fd7e6f893a7a02f91d2c5a2371720ec..b7df5b23d8d72f737c5c6f468c508ec12ade8fd9 100644 |
--- a/base/run_loop.cc |
+++ b/base/run_loop.cc |
@@ -4,29 +4,97 @@ |
#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: |
+ // A vector-based stack is more memory efficient than the default deque-based |
+ // stack as the active RunLoop stack isn't expected to ever have more than a |
+ // few entries. |
+ using RunLoopStack = std::stack<RunLoop*, std::vector<RunLoop*>>; |
+ |
+ ThreadLocalRunLoopState() |
+ : slot_(&ThreadLocalRunLoopState::OnTLSDestruction) {} |
+ |
+ ~ThreadLocalRunLoopState() = delete; |
+ |
+ RunLoopStack& 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; } |
+ |
+ void Reset() { |
+ InternalState* state = static_cast<InternalState*>(slot_.Get()); |
+ if (state) { |
+ slot_.Set(nullptr); |
+ delete state; |
+ } |
+ } |
+ |
+ private: |
+ struct InternalState { |
+ bool allow_nesting = true; |
+ RunLoopStack active_run_loops; |
+ 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() { |
+ // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. |
+ // DCHECK(thread_checker_.CalledOnValidThread()); |
} |
void RunLoop::Run() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
if (!BeforeRun()) |
return; |
@@ -41,12 +109,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 +131,55 @@ void RunLoop::QuitWhenIdle() { |
} |
base::Closure RunLoop::QuitClosure() { |
+ // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. |
+ // DCHECK(thread_checker_.CalledOnValidThread()); |
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(thread_checker_.CalledOnValidThread()); |
return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()); |
} |
+// static |
+void RunLoop::ResetTLSState() { |
+ tls_run_loop_state.Get().Reset(); |
+} |
+ |
+// 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 +187,42 @@ 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; |
+ auto& active_run_loops = 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.OnBeginNestedRunLoop(); |
+ } |
running_ = true; |
return true; |
} |
void RunLoop::AfterRun() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
running_ = false; |
- // Pop RunLoop stack: |
- loop_->run_loop_ = previous_run_loop_; |
+ auto& active_run_loops = tls_run_loop_state.Get().GetActiveRunLoops(); |
+ DCHECK_EQ(active_run_loops.top(), this); |
+ active_run_loops.pop(); |
+ |
+ RunLoop* previous_run_loop = |
+ active_run_loops.empty() ? nullptr : active_run_loops.top(); |
+ loop_->run_loop_ = previous_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 (previous_run_loop && previous_run_loop->quit_called_) |
loop_->QuitNow(); |
} |