1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A component is a large, long-lived set of functionality that may be enabled
// or disabled at runtime. Some examples include: the multizone features,
// MetricsRecorder, or OpencastController. Components may depend on each other
// (ie, a component may call the public methods of other components); the
// Component infrastructure ensures that when a component is disabled, nothing
// that depends on it will call any of its methods until it is enabled again.
//
// Components may be used without a dependency relationship via a weak
// reference. A weak reference does not allow direct access to the component;
// instead, it must be either used to create a strict dependency (see below), or
// be converted to a scoped reference via Try(). Scoped references must be
// checked for validity before use (they are convertible to bool); an invalid
// scoped reference must not be used. Scoped references should be short-lived;
// to encourage this, they are only move-constructible and cannot be copied or
// assigned.
//
// If component Y depends on Component X, then Y has a Dependency reference
// to X. This causes Y to be disabled as X is being disabled (before X's
// OnDisable() method is called). Similarly, this dependency will cause X to be
// enabled when Y is being enabled (X will be enabled before Y's OnEnable()
// method is called). A component may freely access any of its dependencies
// as long as it is enabled. When a component is disabled, it must ensure that
// none of its dependencies will be used again until it is enabled. It is
// recommended to set up dependencies in your component's constructor; it is an
// error to add a dependency to a component that is not disabled.
//
// When a component is disabled, it will first recursively disable any other
// components that depend on it. It will also disable the creation of
// new scoped references. It then waits for all scoped references to be
// destroyed before calling OnDisable() to actually disable the component.
//
// Components MUST be disabled before they are deleted. For ease of use, a
// Destroy() method is provided. When Destroy() is called, it prevents the
// component from being enabled ever again, and then disables it, deleting it
// once it is disabled.
//
// Example usage:
//
// class MetricsRecorder : public Component<MetricsRecorder> {
// public:
// virtual ~MetricsRecorder() {}
// virtual void RecordEvent(const std::string& event) = 0;
// };
//
// class SetupManager : public Component<SetupManager> {
// public:
// virtual ~SetupManager() {}
// virtual int GetMultizoneDelay() = 0;
// };
//
// class Multizone : public Component<Multizone> {
// public:
// virtual ~Multizone() {}
// virtual void DoMultizoneStuff() = 0;
// };
//
// class MetricsRecorderImpl : public MetricsRecorder {
// public:
// void OnEnable() override {
// // ... Enable metrics reporting ...
// OnEnableComplete(true);
// }
//
// // Release all resources; public methods will not be called after this.
// void OnDisable() override {
// OnDisableComplete();
// }
//
// void RecordEvent(const std::string& event) override {
// // ... Record an event ...
// }
// };
//
// class SetupManagerImpl : public SetupManager {
// public:
// void OnEnable() override {
// // ... Enable setup manager ...
// // OnEnableComplete() may be called asynchronously.
// base::ThreadTaskRunnerHandle::Get()->PostTask(
// FROM_HERE, base::Bind(&SetupManagerImpl::CompleteEnable,
// base::Unretained(this)));
// }
//
// void CompleteEnable() {
// OnEnableComplete(true);
// }
//
// void OnDisable() override {
// OnDisableComplete();
// }
//
// int GetMultizoneDelay() override { return 0; }
// };
//
// class MultizoneImpl : public Multizone {
// public:
// MultizoneImpl(const MetricsRecorder::WeakRef& metrics_recorder,
// const SetupManager::WeakRef& setup_manager)
// : metrics_recorder_(metrics_recorder, this),
// setup_manager_(setup_manager) {
// // We can try to use weak deps even before this component is enabled.
// // However, we MUST NOT attempt to use any strong dependencies.
// if (auto setup = setup_manager_.Try()) {
// int delay = setup->GetMultizoneDelay();
// // ... Do something with delay ...
// }
// }
//
// void OnEnable() override {
// // ... Enable multizone ...
// // Can use strong dependencies directly
// metrics_recorder_->RecordEvent("enable multizone");
// OnEnableComplete();
// }
//
// void OnDisable() override {
// // Can still use strong dependencies here. However, this method MUST
// // ensure that strong dependencies will NOT be used after it returns.
// metrics_recorder_->RecordEvent("disable multizone");
// OnDisableComplete();
// }
//
// void DoMultizoneStuff() {
// metrics_recorder_->RecordEvent("multizone stuff");
// // You have to Try() every time you use a weak dependency.
// if (auto setup = setup_manager_.Try()) {
// int delay = setup->GetMultizoneDelay();
// // ... Do something with delay ...
// }
// }
//
// private:
// MetricsRecorder::Dependency metrics_recorder_;
// SetupManager::WeakRef setup_manager_;
// };
#ifndef CHROMECAST_BASE_COMPONENT_COMPONENT_H_
#define CHROMECAST_BASE_COMPONENT_COMPONENT_H_
#include <vector>
#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list_threadsafe.h"
#include "base/threading/thread_checker.h"
#include "chromecast/base/component/component_internal.h"
namespace base {
class SingleThreadTaskRunner;
} // namespace base
namespace chromecast {
class ComponentBase {
public:
class Observer {
public:
// Called when a component finishes being enabled. If the component was
// enabled successfully, |success| will be |true|. Note that access to
// |component| is not guaranteed to be safe; since the observers are
// notified asynchronously, |component| may have been already deleted.
virtual void OnComponentEnabled(ComponentBase* component, bool success) {}
// Called when a component has been disabled. Access to |component| is not
// guaranteed to be safe.
virtual void OnComponentDisabled(ComponentBase* component) {}
protected:
virtual ~Observer() {}
};
virtual ~ComponentBase();
// Enables this component if possible. Attempts to enable all strong
// dependencies first. It is OK to call Disable() while the component is in
// the process of being enabled. All components MUST be created/enabled/
// disabled/destroyed on the same thread.
// Note that enabling a component may occur asynchronously; components must
// always be accessed through a Dependency or WeakReference to ensure safety.
// TODO(kmackay) Consider allowing components to be used on any thread.
void Enable();
// Disables this component; disabling may complete asynchronously. It is OK to
// call Enable() again while the component is being disabled. Note that a
// component MUST be disabled (or never enabled) before it is deleted.
void Disable();
// Deletes this component, disabling it first if necessary.
void Destroy();
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
protected:
ComponentBase();
// Enables the component implementation. This method must set things up so
// that any public method calls are valid, and then call OnEnableComplete(),
// passing in |true| if the enable was successful, |false| otherwise.
// OnEnableComplete() may be called from any thread. OnEnable() will not be
// called again until after the component has been disabled, and will not be
// called during an ongoing OnDisable() call (so if OnDisable() is called,
// then OnEnable() will not be called until OnDisableComplete() has been
// called). This method is called only on the thread that the component was
// created on.
virtual void OnEnable() = 0;
// Disables the component implementation. This is not called until there are
// no more live dependencies, so there will be no more public method calls
// to the component until after OnEnable() is called again. This method must
// do whatever is necessary to ensure that no more calls to dependencies of
// this component will be made, and then call the |disabled_cb|. The
// |disabled_cb| may be called from any thread. This method is called only on
// the thread that the component was created on.
virtual void OnDisable() = 0;
// Handles the success/failure of a call to OnEnable(). When OnEnable() is
// called, it must eventually call OnEnableComplete() (after the component is
// ready to be used by dependents), passing in |true| if the component was
// enabled successfully. If |success| is false, then OnDisable() will be
// called immediately to return the component to a consistent disabled state.
// May be called on any thread.
void OnEnableComplete(bool success);
// Handles the completion of a call to OnDisable(). When OnDisable() is
// called, it must eventually call OnDisableComplete() (after ensuring that
// none of the component's strong dependencies will be used anymore). May be
// called on any thread.
void OnDisableComplete();
private:
friend class subtle::DependencyCount;
friend class subtle::DependencyBase;
friend class subtle::WeakReferenceBase;
enum State {
kStateDisabled,
kStateDisabling,
kStateEnabled,
kStateEnabling,
kStateDestroying
};
void DependencyReady();
void TryOnEnable();
void OnEnableCompleteInternal(bool success);
void DependencyCountDisableComplete();
void TryOnDisable();
void OnDisableCompleteInternal();
void AddDependency(subtle::DependencyBase* dependency);
void StopUsingDependencies();
// Returns |true| if |component| is a transitive dependency of this component.
bool DependsOn(ComponentBase* component);
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scoped_refptr<subtle::DependencyCount> counter_;
std::vector<subtle::DependencyBase*> strong_dependencies_;
State state_;
// |true| when a call to OnEnable()/OnDisable() is in progress.
bool async_call_in_progress_;
int pending_dependency_count_;
const scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_;
DISALLOW_COPY_AND_ASSIGN(ComponentBase);
};
template <typename C>
class StrongDependency : public subtle::DependencyBase {
public:
StrongDependency(const WeakReference<C>& dependency, ComponentBase* dependent)
: subtle::DependencyBase(dependency, dependent) {}
C* operator->() const {
DCHECK(dependency_);
return static_cast<C*>(dependency_);
}
private:
DISALLOW_COPY_AND_ASSIGN(StrongDependency);
};
template <typename C>
class WeakReference : public subtle::WeakReferenceBase {
public:
explicit WeakReference(const C& dependency) : WeakReferenceBase(dependency) {}
explicit WeakReference(const StrongDependency<C>& dependency)
: subtle::WeakReferenceBase(dependency) {}
// Explicitly allow copy.
WeakReference(const WeakReference& other) = default;
WeakReference(WeakReference&& other) = default;
// Disallow assignment.
void operator=(const WeakReference&) = delete;
// Try to get a scoped reference. Expected usage:
// if (auto ref = weak.Try()) {
// // ... use ref ...
// }
subtle::Ref_DO_NOT_DECLARE<C> Try() const {
return subtle::Ref_DO_NOT_DECLARE<C>(counter_);
}
};
template <typename C>
class Component : public ComponentBase {
public:
using WeakRef = WeakReference<C>;
using Dependency = StrongDependency<C>;
Component() = default;
WeakRef GetRef() { return WeakRef(*static_cast<C*>(this)); }
private:
DISALLOW_COPY_AND_ASSIGN(Component);
};
} // namespace chromecast
#endif // CHROMECAST_BASE_COMPONENT_COMPONENT_H_
|