Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: base/run_loop.cc

Issue 2880453003: Introduce RunLoop::Delegate splitting RunLoop/MessageLoop some more. (Closed)
Patch Set: oops, re-invert IsRunning logic Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/run_loop.h ('k') | components/download/internal/scheduler/network_listener_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/run_loop.h" 5 #include "base/run_loop.h"
6 6
7 #include <stack>
8
9 #include "base/bind.h" 7 #include "base/bind.h"
10 #include "base/lazy_instance.h" 8 #include "base/lazy_instance.h"
11 #include "base/observer_list.h" 9 #include "base/threading/thread_local.h"
12 #include "base/threading/thread_local_storage.h"
13 #include "base/tracked_objects.h" 10 #include "base/tracked_objects.h"
14 #include "build/build_config.h" 11 #include "build/build_config.h"
15 12
16 namespace base { 13 namespace base {
17 14
18 namespace { 15 namespace {
19 16
20 class ThreadLocalRunLoopState { 17 LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
21 public:
22 // A vector-based stack is more memory efficient than the default deque-based
23 // stack as the active RunLoop stack isn't expected to ever have more than a
24 // few entries.
25 using RunLoopStack = std::stack<RunLoop*, std::vector<RunLoop*>>;
26
27 ThreadLocalRunLoopState()
28 : slot_(&ThreadLocalRunLoopState::OnTLSDestruction) {}
29
30 ~ThreadLocalRunLoopState() = delete;
31
32 RunLoopStack& GetActiveRunLoops() {
33 return GetOrCreateInternalState()->active_run_loops;
34 }
35
36 ObserverList<RunLoop::NestingObserver>& GetNestingObservers() {
37 InternalState* state = GetOrCreateInternalState();
38 CHECK(state->allow_nesting);
39 return state->nesting_observers;
40 }
41
42 bool IsNestingAllowed() { return GetOrCreateInternalState()->allow_nesting; }
43
44 void DisallowNesting() { GetOrCreateInternalState()->allow_nesting = false; }
45
46 void Reset() {
47 InternalState* state = static_cast<InternalState*>(slot_.Get());
48 if (state) {
49 slot_.Set(nullptr);
50 delete state;
51 }
52 }
53
54 private:
55 struct InternalState {
56 bool allow_nesting = true;
57 RunLoopStack active_run_loops;
58 ObserverList<RunLoop::NestingObserver> nesting_observers;
59 };
60
61 static void OnTLSDestruction(void* internal_state) {
62 delete static_cast<InternalState*>(internal_state);
63 }
64
65 InternalState* GetOrCreateInternalState() {
66 InternalState* state = static_cast<InternalState*>(slot_.Get());
67 if (!state) {
68 state = new InternalState;
69 slot_.Set(static_cast<void*>(state));
70 }
71 return state;
72 }
73
74 ThreadLocalStorage::Slot slot_;
75
76 DISALLOW_COPY_AND_ASSIGN(ThreadLocalRunLoopState);
77 };
78
79 LazyInstance<ThreadLocalRunLoopState>::Leaky tls_run_loop_state =
80 LAZY_INSTANCE_INITIALIZER; 18 LAZY_INSTANCE_INITIALIZER;
81 19
82 } // namespace 20 } // namespace
83 21
84 RunLoop::RunLoop() 22 RunLoop::Delegate::Delegate() {
85 : loop_(MessageLoop::current()), 23 // The Delegate can be created on another thread. It is only bound in
86 weak_factory_(this) { 24 // RegisterDelegateForCurrentThread().
87 DCHECK(loop_); 25 DETACH_FROM_THREAD(bound_thread_checker_);
26 }
27
28 RunLoop::Delegate::~Delegate() {
29 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
30 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
31 // be on its creation thread (e.g. a Thread that fails to start) and
32 // shouldn't disrupt that thread's state.
33 if (bound_)
34 tls_delegate.Get().Set(nullptr);
35 }
36
37 RunLoop* RunLoop::Delegate::Client::GetTopMostRunLoop() const {
38 DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
39 DCHECK(outer_->bound_);
40 return outer_->active_run_loops_.empty() ? nullptr
41 : outer_->active_run_loops_.top();
42 }
43
44 bool RunLoop::Delegate::Client::IsNested() const {
45 DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
46 DCHECK(outer_->bound_);
47 return outer_->active_run_loops_.size() > 1;
48 }
49
50 RunLoop::Delegate::Client::Client(Delegate* outer) : outer_(outer) {}
51
52 RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread(
53 Delegate* delegate) {
54 // Bind |delegate| to this thread.
55 DCHECK(!delegate->bound_);
56 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
57
58 // There can only be one RunLoop::Delegate per thread.
59 DCHECK(!tls_delegate.Get().Get());
60 tls_delegate.Get().Set(delegate);
61 delegate->bound_ = true;
62
63 return &delegate->client_interface_;
64 }
65
66 RunLoop::RunLoop() : delegate_(tls_delegate.Get().Get()), weak_factory_(this) {
67 // A RunLoop::Delegate must be bound to this thread prior to using RunLoop.
68 DCHECK(delegate_);
88 } 69 }
89 70
90 RunLoop::~RunLoop() { 71 RunLoop::~RunLoop() {
91 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 72 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
92 // DCHECK(thread_checker_.CalledOnValidThread()); 73 // DCHECK(thread_checker_.CalledOnValidThread());
93 } 74 }
94 75
95 void RunLoop::Run() { 76 void RunLoop::Run() {
96 DCHECK(thread_checker_.CalledOnValidThread()); 77 DCHECK(thread_checker_.CalledOnValidThread());
97 78
98 if (!BeforeRun()) 79 if (!BeforeRun())
99 return; 80 return;
100 81
101 // Use task stopwatch to exclude the loop run time from the current task, if 82 // Use task stopwatch to exclude the loop run time from the current task, if
102 // any. 83 // any.
103 tracked_objects::TaskStopwatch stopwatch; 84 tracked_objects::TaskStopwatch stopwatch;
104 stopwatch.Start(); 85 stopwatch.Start();
105 loop_->RunHandler(); 86 delegate_->Run();
106 stopwatch.Stop(); 87 stopwatch.Stop();
107 88
108 AfterRun(); 89 AfterRun();
109 } 90 }
110 91
111 void RunLoop::RunUntilIdle() { 92 void RunLoop::RunUntilIdle() {
112 DCHECK(thread_checker_.CalledOnValidThread()); 93 DCHECK(thread_checker_.CalledOnValidThread());
113 94
114 quit_when_idle_received_ = true; 95 quit_when_idle_received_ = true;
115 Run(); 96 Run();
116 } 97 }
117 98
118 void RunLoop::Quit() { 99 void RunLoop::Quit() {
119 DCHECK(thread_checker_.CalledOnValidThread()); 100 DCHECK(thread_checker_.CalledOnValidThread());
120 101
121 quit_called_ = true; 102 quit_called_ = true;
122 if (running_ && loop_->run_loop_ == this) { 103 if (running_ && delegate_->active_run_loops_.top() == this) {
123 // This is the inner-most RunLoop, so quit now. 104 // This is the inner-most RunLoop, so quit now.
124 loop_->QuitNow(); 105 delegate_->Quit();
125 } 106 }
126 } 107 }
127 108
128 void RunLoop::QuitWhenIdle() { 109 void RunLoop::QuitWhenIdle() {
129 DCHECK(thread_checker_.CalledOnValidThread()); 110 DCHECK(thread_checker_.CalledOnValidThread());
130 quit_when_idle_received_ = true; 111 quit_when_idle_received_ = true;
131 } 112 }
132 113
133 base::Closure RunLoop::QuitClosure() { 114 base::Closure RunLoop::QuitClosure() {
134 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 115 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
135 // DCHECK(thread_checker_.CalledOnValidThread()); 116 // DCHECK(thread_checker_.CalledOnValidThread());
136 return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()); 117 return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr());
137 } 118 }
138 119
139 base::Closure RunLoop::QuitWhenIdleClosure() { 120 base::Closure RunLoop::QuitWhenIdleClosure() {
140 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 121 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
141 // DCHECK(thread_checker_.CalledOnValidThread()); 122 // DCHECK(thread_checker_.CalledOnValidThread());
142 return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()); 123 return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr());
143 } 124 }
144 125
145 // static 126 // static
146 void RunLoop::ResetTLSState() {
147 tls_run_loop_state.Get().Reset();
148 }
149
150 // static
151 bool RunLoop::IsRunningOnCurrentThread() { 127 bool RunLoop::IsRunningOnCurrentThread() {
152 return !tls_run_loop_state.Get().GetActiveRunLoops().empty(); 128 Delegate* delegate = tls_delegate.Get().Get();
129 return delegate && !delegate->active_run_loops_.empty();
153 } 130 }
154 131
155 // static 132 // static
156 bool RunLoop::IsNestedOnCurrentThread() { 133 bool RunLoop::IsNestedOnCurrentThread() {
157 return tls_run_loop_state.Get().GetActiveRunLoops().size() > 1; 134 Delegate* delegate = tls_delegate.Get().Get();
135 return delegate && delegate->active_run_loops_.size() > 1;
158 } 136 }
159 137
160 // static 138 // static
161 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) { 139 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
162 tls_run_loop_state.Get().GetNestingObservers().AddObserver(observer); 140 Delegate* delegate = tls_delegate.Get().Get();
141 DCHECK(delegate);
142 CHECK(delegate->allow_nesting_);
143 delegate->nesting_observers_.AddObserver(observer);
163 } 144 }
164 145
165 // static 146 // static
166 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) { 147 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
167 tls_run_loop_state.Get().GetNestingObservers().RemoveObserver(observer); 148 Delegate* delegate = tls_delegate.Get().Get();
149 DCHECK(delegate);
150 CHECK(delegate->allow_nesting_);
151 delegate->nesting_observers_.RemoveObserver(observer);
168 } 152 }
169 153
170 // static 154 // static
171 bool RunLoop::IsNestingAllowedOnCurrentThread() { 155 bool RunLoop::IsNestingAllowedOnCurrentThread() {
172 return tls_run_loop_state.Get().IsNestingAllowed(); 156 return tls_delegate.Get().Get()->allow_nesting_;
173 } 157 }
174 158
175 // static 159 // static
176 void RunLoop::DisallowNestingOnCurrentThread() { 160 void RunLoop::DisallowNestingOnCurrentThread() {
177 tls_run_loop_state.Get().DisallowNesting(); 161 tls_delegate.Get().Get()->allow_nesting_ = false;
178 } 162 }
179 163
180 bool RunLoop::BeforeRun() { 164 bool RunLoop::BeforeRun() {
181 DCHECK(thread_checker_.CalledOnValidThread()); 165 DCHECK(thread_checker_.CalledOnValidThread());
182 166
183 DCHECK(!run_called_); 167 DCHECK(!run_called_);
184 run_called_ = true; 168 run_called_ = true;
185 169
186 // Allow Quit to be called before Run. 170 // Allow Quit to be called before Run.
187 if (quit_called_) 171 if (quit_called_)
188 return false; 172 return false;
189 173
190 auto& active_run_loops = tls_run_loop_state.Get().GetActiveRunLoops(); 174 auto& active_run_loops_ = delegate_->active_run_loops_;
191 active_run_loops.push(this); 175 active_run_loops_.push(this);
192 176
193 const bool is_nested = active_run_loops.size() > 1; 177 const bool is_nested = active_run_loops_.size() > 1;
194
195 // TODO(gab): Break the inter-dependency between MessageLoop and RunLoop
196 // further. http://crbug.com/703346
197 loop_->run_loop_ = this;
198 loop_->is_nested_ = is_nested;
199 178
200 if (is_nested) { 179 if (is_nested) {
201 CHECK(tls_run_loop_state.Get().IsNestingAllowed()); 180 CHECK(delegate_->allow_nesting_);
202 for (auto& observer : tls_run_loop_state.Get().GetNestingObservers()) 181 for (auto& observer : delegate_->nesting_observers_)
203 observer.OnBeginNestedRunLoop(); 182 observer.OnBeginNestedRunLoop();
204 } 183 }
205 184
206 running_ = true; 185 running_ = true;
207 return true; 186 return true;
208 } 187 }
209 188
210 void RunLoop::AfterRun() { 189 void RunLoop::AfterRun() {
211 DCHECK(thread_checker_.CalledOnValidThread()); 190 DCHECK(thread_checker_.CalledOnValidThread());
212 191
213 running_ = false; 192 running_ = false;
214 193
215 auto& active_run_loops = tls_run_loop_state.Get().GetActiveRunLoops(); 194 auto& active_run_loops_ = delegate_->active_run_loops_;
216 DCHECK_EQ(active_run_loops.top(), this); 195 DCHECK_EQ(active_run_loops_.top(), this);
217 active_run_loops.pop(); 196 active_run_loops_.pop();
218 197
219 RunLoop* previous_run_loop = 198 RunLoop* previous_run_loop =
220 active_run_loops.empty() ? nullptr : active_run_loops.top(); 199 active_run_loops_.empty() ? nullptr : active_run_loops_.top();
221 loop_->run_loop_ = previous_run_loop;
222 loop_->is_nested_ = active_run_loops.size() > 1;
223 200
224 // Execute deferred QuitNow, if any: 201 // Execute deferred QuitNow, if any:
225 if (previous_run_loop && previous_run_loop->quit_called_) 202 if (previous_run_loop && previous_run_loop->quit_called_)
226 loop_->QuitNow(); 203 delegate_->Quit();
227 } 204 }
228 205
229 } // namespace base 206 } // namespace base
OLDNEW
« no previous file with comments | « base/run_loop.h ('k') | components/download/internal/scheduler/network_listener_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698