1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromecast/base/component/component.h"
6
7 #include <set>
8 #include <utility>
9
10 #include "base/atomicops.h"
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/threading/thread_task_runner_handle.h"
16
17 namespace chromecast {
18
19 namespace {
20
21 const base::subtle::AtomicWord kEnabledBit = 0x40000000;
22
23 } // namespace
24
25 namespace subtle {
26
27 class DependencyCount : public base::RefCountedThreadSafe<DependencyCount> {
28 public:
DependencyCount(ComponentBase * component)29 explicit DependencyCount(ComponentBase* component)
30 : component_(component),
31 task_runner_(base::ThreadTaskRunnerHandle::Get()),
32 dep_count_(0),
33 disabling_(false) {
34 DCHECK(component_);
35 }
36
Detach()37 void Detach() {
38 DCHECK(task_runner_->BelongsToCurrentThread());
39 component_ = nullptr;
40 }
41
Disable()42 void Disable() {
43 DCHECK(task_runner_->BelongsToCurrentThread());
44 DCHECK(!disabling_);
45 disabling_ = true;
46
47 std::set<DependencyBase*> dependents(strong_dependents_);
48 for (DependencyBase* dependent : dependents)
49 dependent->Disable();
50
51 while (true) {
52 AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_);
53 AtomicWord old_deps = base::subtle::Acquire_CompareAndSwap(
54 &dep_count_, deps, deps & ~kEnabledBit);
55 if (old_deps == deps) {
56 if ((deps & ~kEnabledBit) == 0)
57 DisableComplete();
58 return;
59 }
60 }
61 }
62
Enable()63 void Enable() {
64 DCHECK(task_runner_->BelongsToCurrentThread());
65 disabling_ = false;
66 while (true) {
67 AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_);
68 DCHECK(!(deps & kEnabledBit));
69 AtomicWord old_deps = base::subtle::Release_CompareAndSwap(
70 &dep_count_, deps, deps | kEnabledBit);
71 if (old_deps == deps)
72 break;
73 }
74
75 for (DependencyBase* dependent : strong_dependents_)
76 dependent->Ready(component_);
77 }
78
WeakAcquireDep()79 ComponentBase* WeakAcquireDep() {
80 while (true) {
81 AtomicWord deps = base::subtle::NoBarrier_Load(&dep_count_);
82 if (!(deps & kEnabledBit))
83 return nullptr;
84 AtomicWord old_deps =
85 base::subtle::Acquire_CompareAndSwap(&dep_count_, deps, deps + 1);
86 // We depend on the fact that a component must be disabled (meaning that
87 // we will never reach this point) before it is destroyed. Therefore if
88 // we do reach this point, it is safe to return the raw pointer.
89 if (old_deps == deps)
90 return component_;
91 }
92 }
93
StrongAcquireDep(DependencyBase * dependent)94 void StrongAcquireDep(DependencyBase* dependent) {
95 DCHECK(dependent);
96 DCHECK(task_runner_->BelongsToCurrentThread());
97 if (!component_) {
98 dependent->Disable();
99 return;
100 }
101
102 strong_dependents_.insert(dependent);
103 AtomicWord count = base::subtle::NoBarrier_AtomicIncrement(&dep_count_, 1);
104 DCHECK_GT(count, 0);
105
106 if (count & kEnabledBit) {
107 dependent->Ready(component_);
108 } else {
109 component_->Enable();
110 }
111 }
112
StrongReleaseDep(DependencyBase * dependent)113 void StrongReleaseDep(DependencyBase* dependent) {
114 DCHECK(dependent);
115 DCHECK(task_runner_->BelongsToCurrentThread());
116 strong_dependents_.erase(dependent);
117 ReleaseDep();
118 }
119
ReleaseDep()120 void ReleaseDep() {
121 AtomicWord after = base::subtle::Barrier_AtomicIncrement(&dep_count_, -1);
122 DCHECK_GE(after, 0);
123 if (after == 0)
124 DisableComplete();
125 }
126
DependsOn(ComponentBase * component)127 bool DependsOn(ComponentBase* component) {
128 DCHECK(task_runner_->BelongsToCurrentThread());
129 if (!component_)
130 return false;
131 if (component_ == component)
132 return true;
133 return component_->DependsOn(component);
134 }
135
136 private:
137 friend class base::RefCountedThreadSafe<DependencyCount>;
138 using AtomicWord = base::subtle::AtomicWord;
139
~DependencyCount()140 ~DependencyCount() {}
141
DisableComplete()142 void DisableComplete() {
143 if (!task_runner_->BelongsToCurrentThread()) {
144 task_runner_->PostTask(
145 FROM_HERE, base::BindOnce(&DependencyCount::DisableComplete, this));
146 return;
147 }
148 // Need to make sure that Enable() was not called in the meantime.
149 if (base::subtle::NoBarrier_Load(&dep_count_) != 0 || !disabling_)
150 return;
151 // Ensure that we don't call DisableComplete() more than once per Disable().
152 disabling_ = false;
153 DCHECK(component_);
154 DCHECK(strong_dependents_.empty());
155 component_->DependencyCountDisableComplete();
156 }
157
158 ComponentBase* component_;
159 const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
160 AtomicWord dep_count_;
161 bool disabling_;
162 std::set<DependencyBase*> strong_dependents_;
163
164 DISALLOW_COPY_AND_ASSIGN(DependencyCount);
165 };
166
DependencyBase(const WeakReferenceBase & dependency,ComponentBase * dependent)167 DependencyBase::DependencyBase(const WeakReferenceBase& dependency,
168 ComponentBase* dependent)
169 : dependent_(dependent),
170 dependency_(nullptr),
171 counter_(dependency.counter_) {
172 DCHECK(dependent_);
173 dependent_->AddDependency(this);
174 }
175
~DependencyBase()176 DependencyBase::~DependencyBase() {}
177
StartUsing()178 void DependencyBase::StartUsing() {
179 DCHECK(thread_checker_.CalledOnValidThread());
180 DCHECK(!dependency_);
181 counter_->StrongAcquireDep(this);
182 }
183
StopUsing()184 void DependencyBase::StopUsing() {
185 DCHECK(thread_checker_.CalledOnValidThread());
186 if (!dependency_)
187 return;
188 dependency_ = nullptr;
189 counter_->StrongReleaseDep(this);
190 }
191
Ready(ComponentBase * dependency)192 void DependencyBase::Ready(ComponentBase* dependency) {
193 DCHECK(thread_checker_.CalledOnValidThread());
194 DCHECK(!dependency_);
195 DCHECK(dependency);
196 dependency_ = dependency;
197 dependent_->DependencyReady();
198 }
199
Disable()200 void DependencyBase::Disable() {
201 DCHECK(thread_checker_.CalledOnValidThread());
202 dependent_->Disable();
203 }
204
DependsOn(ComponentBase * component)205 bool DependencyBase::DependsOn(ComponentBase* component) {
206 return counter_->DependsOn(component);
207 }
208
WeakReferenceBase(const ComponentBase & dependency)209 WeakReferenceBase::WeakReferenceBase(const ComponentBase& dependency)
210 : counter_(dependency.counter_) {
211 DCHECK(counter_);
212 }
213
WeakReferenceBase(const DependencyBase & dependency)214 WeakReferenceBase::WeakReferenceBase(const DependencyBase& dependency)
215 : counter_(dependency.counter_) {
216 DCHECK(counter_);
217 }
218
WeakReferenceBase(const WeakReferenceBase & other)219 WeakReferenceBase::WeakReferenceBase(const WeakReferenceBase& other)
220 : counter_(other.counter_) {
221 DCHECK(counter_);
222 }
223
WeakReferenceBase(WeakReferenceBase && other)224 WeakReferenceBase::WeakReferenceBase(WeakReferenceBase&& other)
225 : counter_(std::move(other.counter_)) {
226 DCHECK(counter_);
227 }
228
~WeakReferenceBase()229 WeakReferenceBase::~WeakReferenceBase() {}
230
ScopedReferenceBase(const scoped_refptr<DependencyCount> & counter)231 ScopedReferenceBase::ScopedReferenceBase(
232 const scoped_refptr<DependencyCount>& counter)
233 : counter_(counter) {
234 DCHECK(counter_);
235 dependency_ = counter_->WeakAcquireDep();
236 }
237
ScopedReferenceBase(ScopedReferenceBase && other)238 ScopedReferenceBase::ScopedReferenceBase(ScopedReferenceBase&& other)
239 : counter_(std::move(other.counter_)), dependency_(other.dependency_) {
240 DCHECK(counter_);
241 other.dependency_ = nullptr;
242 }
243
~ScopedReferenceBase()244 ScopedReferenceBase::~ScopedReferenceBase() {
245 if (dependency_)
246 counter_->ReleaseDep();
247 }
248
249 } // namespace subtle
250
ComponentBase()251 ComponentBase::ComponentBase()
252 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
253 state_(kStateDisabled),
254 async_call_in_progress_(false),
255 pending_dependency_count_(0),
256 observers_(new base::ObserverListThreadSafe<Observer>()) {
257 counter_ = new subtle::DependencyCount(this);
258 }
259
~ComponentBase()260 ComponentBase::~ComponentBase() {
261 DCHECK(task_runner_->BelongsToCurrentThread());
262 DCHECK_EQ(kStateDisabled, state_) << "Components must be disabled "
263 << "before being destroyed";
264 counter_->Detach();
265 }
266
Enable()267 void ComponentBase::Enable() {
268 DCHECK(task_runner_->BelongsToCurrentThread());
269 if (state_ == kStateEnabling || state_ == kStateEnabled ||
270 state_ == kStateDestroying) {
271 return;
272 }
273 state_ = kStateEnabling;
274
275 if (strong_dependencies_.empty()) {
276 TryOnEnable();
277 } else {
278 // Enable all strong dependencies first.
279 pending_dependency_count_ = strong_dependencies_.size();
280 for (subtle::DependencyBase* dependency : strong_dependencies_)
281 dependency->StartUsing();
282 }
283 }
284
DependencyReady()285 void ComponentBase::DependencyReady() {
286 DCHECK(task_runner_->BelongsToCurrentThread());
287 if (state_ != kStateEnabling)
288 return;
289 DCHECK_GT(pending_dependency_count_, 0);
290 --pending_dependency_count_;
291 if (pending_dependency_count_ == 0)
292 TryOnEnable();
293 }
294
TryOnEnable()295 void ComponentBase::TryOnEnable() {
296 DCHECK_EQ(kStateEnabling, state_);
297 if (async_call_in_progress_)
298 return;
299 async_call_in_progress_ = true;
300 OnEnable();
301 }
302
OnEnableComplete(bool success)303 void ComponentBase::OnEnableComplete(bool success) {
304 // Always post a task, to prevent the stack from getting too deep.
305 task_runner_->PostTask(
306 FROM_HERE, base::BindOnce(&ComponentBase::OnEnableCompleteInternal,
307 base::Unretained(this), success));
308 }
309
OnEnableCompleteInternal(bool success)310 void ComponentBase::OnEnableCompleteInternal(bool success) {
311 async_call_in_progress_ = false;
312 DCHECK(state_ == kStateEnabling || state_ == kStateDisabling ||
313 state_ == kStateDestroying);
314 if (state_ != kStateEnabling) {
315 if (success) {
316 TryOnDisable();
317 } else {
318 OnDisableCompleteInternal();
319 }
320 return;
321 }
322
323 if (success) {
324 state_ = kStateEnabled;
325 counter_->Enable();
326 } else {
327 Disable();
328 }
329 observers_->Notify(FROM_HERE, &Observer::OnComponentEnabled, this, success);
330 }
331
Destroy()332 void ComponentBase::Destroy() {
333 DCHECK(task_runner_->BelongsToCurrentThread());
334 DCHECK_NE(kStateDestroying, state_);
335 if (state_ == kStateDisabled) {
336 delete this;
337 } else {
338 bool should_disable = (state_ != kStateDisabling);
339 state_ = kStateDestroying;
340 if (should_disable)
341 counter_->Disable();
342 }
343 }
344
AddObserver(Observer * observer)345 void ComponentBase::AddObserver(Observer* observer) {
346 DCHECK(observer);
347 observers_->AddObserver(observer);
348 }
349
RemoveObserver(Observer * observer)350 void ComponentBase::RemoveObserver(Observer* observer) {
351 observers_->RemoveObserver(observer);
352 }
353
Disable()354 void ComponentBase::Disable() {
355 DCHECK(task_runner_->BelongsToCurrentThread());
356 if (state_ == kStateDisabling || state_ == kStateDisabled ||
357 state_ == kStateDestroying) {
358 return;
359 }
360 state_ = kStateDisabling;
361 counter_->Disable();
362 }
363
DependencyCountDisableComplete()364 void ComponentBase::DependencyCountDisableComplete() {
365 DCHECK(task_runner_->BelongsToCurrentThread());
366 if (state_ == kStateDisabling || state_ == kStateDestroying)
367 TryOnDisable();
368 }
369
TryOnDisable()370 void ComponentBase::TryOnDisable() {
371 DCHECK(state_ == kStateDisabling || state_ == kStateDestroying);
372 if (async_call_in_progress_)
373 return;
374 async_call_in_progress_ = true;
375 OnDisable();
376 }
377
OnDisableComplete()378 void ComponentBase::OnDisableComplete() {
379 // Always post a task, to prevent calls to Disable() from within Enable().
380 task_runner_->PostTask(
381 FROM_HERE, base::BindOnce(&ComponentBase::OnDisableCompleteInternal,
382 base::Unretained(this)));
383 }
384
OnDisableCompleteInternal()385 void ComponentBase::OnDisableCompleteInternal() {
386 async_call_in_progress_ = false;
387 DCHECK(state_ == kStateEnabling || state_ == kStateDisabling ||
388 state_ == kStateDestroying);
389 if (state_ == kStateEnabling) {
390 TryOnEnable();
391 return;
392 }
393
394 if (state_ == kStateDestroying) {
395 StopUsingDependencies();
396 observers_->Notify(FROM_HERE, &Observer::OnComponentDisabled, this);
397 state_ = kStateDisabled;
398 delete this;
399 } else {
400 state_ = kStateDisabled;
401 StopUsingDependencies();
402 observers_->Notify(FROM_HERE, &Observer::OnComponentDisabled, this);
403 }
404 }
405
AddDependency(subtle::DependencyBase * dependency)406 void ComponentBase::AddDependency(subtle::DependencyBase* dependency) {
407 DCHECK_EQ(kStateDisabled, state_);
408 DCHECK(!dependency->DependsOn(this)) << "Circular dependency detected";
409 strong_dependencies_.push_back(dependency);
410 }
411
StopUsingDependencies()412 void ComponentBase::StopUsingDependencies() {
413 for (subtle::DependencyBase* dependency : strong_dependencies_)
414 dependency->StopUsing();
415 }
416
DependsOn(ComponentBase * component)417 bool ComponentBase::DependsOn(ComponentBase* component) {
418 for (subtle::DependencyBase* dependency : strong_dependencies_) {
419 if (dependency->DependsOn(component))
420 return true;
421 }
422 return false;
423 }
424
425 } // namespace chromecast
426