Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #if !defined(StateWatching_h_)
8 : #define StateWatching_h_
9 :
10 : #include "mozilla/AbstractThread.h"
11 : #include "mozilla/Logging.h"
12 : #include "mozilla/TaskDispatcher.h"
13 : #include "mozilla/UniquePtr.h"
14 : #include "mozilla/Unused.h"
15 :
16 : #include "nsISupportsImpl.h"
17 :
18 : /*
19 : * The state-watching machinery automates the process of responding to changes
20 : * in various pieces of state.
21 : *
22 : * A standard programming pattern is as follows:
23 : *
24 : * mFoo = ...;
25 : * NotifyStuffChanged();
26 : * ...
27 : * mBar = ...;
28 : * NotifyStuffChanged();
29 : *
30 : * This pattern is error-prone and difficult to audit because it requires the
31 : * programmer to manually trigger the update routine. This can be especially
32 : * problematic when the update routine depends on numerous pieces of state, and
33 : * when that state is modified across a variety of helper methods. In these
34 : * cases the responsibility for invoking the routine is often unclear, causing
35 : * developers to scatter calls to it like pixie dust. This can result in
36 : * duplicate invocations (which is wasteful) and missing invocations in corner-
37 : * cases (which is a source of bugs).
38 : *
39 : * This file provides a set of primitives that automatically handle updates and
40 : * allow the programmers to explicitly construct a graph of state dependencies.
41 : * When used correctly, it eliminates the guess-work and wasted cycles described
42 : * above.
43 : *
44 : * There are two basic pieces:
45 : * (1) Objects that can be watched for updates. These inherit WatchTarget.
46 : * (2) Objects that receive objects and trigger processing. These inherit
47 : * AbstractWatcher. In the current machinery, these exist only internally
48 : * within the WatchManager, though that could change.
49 : *
50 : * Note that none of this machinery is thread-safe - it must all happen on the
51 : * same owning thread. To solve multi-threaded use-cases, use state mirroring
52 : * and watch the mirrored value.
53 : *
54 : * Given that semantics may change and comments tend to go out of date, we
55 : * deliberately don't provide usage examples here. Grep around to find them.
56 : */
57 :
58 : namespace mozilla {
59 :
60 : extern LazyLogModule gStateWatchingLog;
61 :
62 : #define WATCH_LOG(x, ...) \
63 : MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
64 :
65 : /*
66 : * AbstractWatcher is a superclass from which all watchers must inherit.
67 : */
68 : class AbstractWatcher
69 : {
70 : public:
71 3 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
72 1 : AbstractWatcher() : mDestroyed(false) {}
73 0 : bool IsDestroyed() { return mDestroyed; }
74 : virtual void Notify() = 0;
75 :
76 : protected:
77 0 : virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
78 : bool mDestroyed;
79 : };
80 :
81 : /*
82 : * WatchTarget is a superclass from which all watchable things must inherit.
83 : * Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
84 : * needs only to invoke NotifyWatchers when something changes.
85 : *
86 : * The functionality that this class provides is not threadsafe, and should only
87 : * be used on the thread that owns that WatchTarget.
88 : */
89 0 : class WatchTarget
90 : {
91 : public:
92 2 : explicit WatchTarget(const char* aName) : mName(aName) {}
93 :
94 2 : void AddWatcher(AbstractWatcher* aWatcher)
95 : {
96 2 : MOZ_ASSERT(!mWatchers.Contains(aWatcher));
97 2 : mWatchers.AppendElement(aWatcher);
98 2 : }
99 :
100 0 : void RemoveWatcher(AbstractWatcher* aWatcher)
101 : {
102 0 : MOZ_ASSERT(mWatchers.Contains(aWatcher));
103 0 : mWatchers.RemoveElement(aWatcher);
104 0 : }
105 :
106 : protected:
107 0 : void NotifyWatchers()
108 : {
109 0 : WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
110 0 : PruneWatchers();
111 0 : for (size_t i = 0; i < mWatchers.Length(); ++i) {
112 0 : mWatchers[i]->Notify();
113 : }
114 0 : }
115 :
116 : private:
117 : // We don't have Watchers explicitly unregister themselves when they die,
118 : // because then they'd need back-references to all the WatchTargets they're
119 : // subscribed to, and WatchTargets aren't reference-counted. So instead we
120 : // just prune dead ones at appropriate times, which works just fine.
121 0 : void PruneWatchers()
122 : {
123 0 : for (int i = mWatchers.Length() - 1; i >= 0; --i) {
124 0 : if (mWatchers[i]->IsDestroyed()) {
125 0 : mWatchers.RemoveElementAt(i);
126 : }
127 : }
128 0 : }
129 :
130 : nsTArray<RefPtr<AbstractWatcher>> mWatchers;
131 :
132 : protected:
133 : const char* mName;
134 : };
135 :
136 : /*
137 : * Watchable is a wrapper class that turns any primitive into a WatchTarget.
138 : */
139 : template<typename T>
140 0 : class Watchable : public WatchTarget
141 : {
142 : public:
143 2 : Watchable(const T& aInitialValue, const char* aName)
144 2 : : WatchTarget(aName), mValue(aInitialValue) {}
145 :
146 0 : const T& Ref() const { return mValue; }
147 0 : operator const T&() const { return Ref(); }
148 0 : Watchable& operator=(const T& aNewValue)
149 : {
150 0 : if (aNewValue != mValue) {
151 0 : mValue = aNewValue;
152 0 : NotifyWatchers();
153 : }
154 :
155 0 : return *this;
156 : }
157 :
158 : private:
159 : Watchable(const Watchable& aOther); // Not implemented
160 : Watchable& operator=(const Watchable& aOther); // Not implemented
161 :
162 : T mValue;
163 : };
164 :
165 : // Manager class for state-watching. Declare one of these in any class for which
166 : // you want to invoke method callbacks.
167 : //
168 : // Internally, WatchManager maintains one AbstractWatcher per callback method.
169 : // Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple.
170 : // This causes an AbstractWatcher for |Callback| to be instantiated if it doesn't
171 : // already exist, and registers it with |WatchTarget|.
172 : //
173 : // Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire
174 : // watch callbacks no more than once per task, once all other operations for that
175 : // task have been completed.
176 : //
177 : // WatchManager<OwnerType> is intended to be declared as a member of |OwnerType|
178 : // objects. Given that, it and its owned objects can't hold permanent strong refs to
179 : // the owner, since that would keep the owner alive indefinitely. Instead, it
180 : // _only_ holds strong refs while waiting for Direct Tasks to fire. This ensures
181 : // that everything is kept alive just long enough.
182 : template <typename OwnerType>
183 : class WatchManager
184 : {
185 : public:
186 : typedef void(OwnerType::*CallbackMethod)();
187 1 : explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
188 1 : : mOwner(aOwner), mOwnerThread(aOwnerThread) {}
189 :
190 0 : ~WatchManager()
191 : {
192 0 : if (!IsShutdown()) {
193 0 : Shutdown();
194 : }
195 0 : }
196 :
197 0 : bool IsShutdown() const { return !mOwner; }
198 :
199 : // Shutdown needs to happen on mOwnerThread. If the WatchManager will be
200 : // destroyed on a different thread, Shutdown() must be called manually.
201 0 : void Shutdown()
202 : {
203 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
204 0 : for (size_t i = 0; i < mWatchers.Length(); ++i) {
205 0 : mWatchers[i]->Destroy();
206 : }
207 0 : mWatchers.Clear();
208 0 : mOwner = nullptr;
209 0 : }
210 :
211 2 : void Watch(WatchTarget& aTarget, CallbackMethod aMethod)
212 : {
213 2 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
214 2 : aTarget.AddWatcher(&EnsureWatcher(aMethod));
215 2 : }
216 :
217 0 : void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod)
218 : {
219 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
220 0 : PerCallbackWatcher* watcher = GetWatcher(aMethod);
221 0 : MOZ_ASSERT(watcher);
222 0 : aTarget.RemoveWatcher(watcher);
223 0 : }
224 :
225 0 : void ManualNotify(CallbackMethod aMethod)
226 : {
227 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
228 0 : PerCallbackWatcher* watcher = GetWatcher(aMethod);
229 0 : MOZ_ASSERT(watcher);
230 0 : watcher->Notify();
231 0 : }
232 :
233 : private:
234 : class PerCallbackWatcher : public AbstractWatcher
235 : {
236 : public:
237 1 : PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread, CallbackMethod aMethod)
238 1 : : mOwner(aOwner), mOwnerThread(aOwnerThread), mCallbackMethod(aMethod) {}
239 :
240 0 : void Destroy()
241 : {
242 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
243 0 : mDestroyed = true;
244 0 : mOwner = nullptr;
245 0 : }
246 :
247 0 : void Notify() override
248 : {
249 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
250 0 : MOZ_DIAGNOSTIC_ASSERT(mOwner, "mOwner is only null after destruction, "
251 : "at which point we shouldn't be notified");
252 0 : if (mStrongRef) {
253 : // We've already got a notification job in the pipe.
254 0 : return;
255 : }
256 0 : mStrongRef = mOwner; // Hold the owner alive while notifying.
257 :
258 : // Queue up our notification jobs to run in a stable state.
259 0 : mOwnerThread->TailDispatcher().AddDirectTask(
260 : NewRunnableMethod("WatchManager::PerCallbackWatcher::DoNotify",
261 : this,
262 : &PerCallbackWatcher::DoNotify));
263 : }
264 :
265 1 : bool CallbackMethodIs(CallbackMethod aMethod) const
266 : {
267 1 : return mCallbackMethod == aMethod;
268 : }
269 :
270 : private:
271 0 : ~PerCallbackWatcher() {}
272 :
273 0 : void DoNotify()
274 : {
275 0 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
276 0 : MOZ_ASSERT(mStrongRef);
277 0 : RefPtr<OwnerType> ref = mStrongRef.forget();
278 0 : if (!mDestroyed) {
279 0 : ((*ref).*mCallbackMethod)();
280 : }
281 0 : }
282 :
283 : OwnerType* mOwner; // Never null.
284 : RefPtr<OwnerType> mStrongRef; // Only non-null when notifying.
285 : RefPtr<AbstractThread> mOwnerThread;
286 : CallbackMethod mCallbackMethod;
287 : };
288 :
289 2 : PerCallbackWatcher* GetWatcher(CallbackMethod aMethod)
290 : {
291 2 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
292 2 : for (size_t i = 0; i < mWatchers.Length(); ++i) {
293 1 : if (mWatchers[i]->CallbackMethodIs(aMethod)) {
294 1 : return mWatchers[i];
295 : }
296 : }
297 1 : return nullptr;
298 : }
299 :
300 2 : PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod)
301 : {
302 2 : MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
303 2 : PerCallbackWatcher* watcher = GetWatcher(aMethod);
304 2 : if (watcher) {
305 1 : return *watcher;
306 : }
307 2 : watcher = mWatchers.AppendElement(new PerCallbackWatcher(mOwner, mOwnerThread, aMethod))->get();
308 1 : return *watcher;
309 : }
310 :
311 : nsTArray<RefPtr<PerCallbackWatcher>> mWatchers;
312 : OwnerType* mOwner;
313 : RefPtr<AbstractThread> mOwnerThread;
314 : };
315 :
316 : #undef WATCH_LOG
317 :
318 : } // namespace mozilla
319 :
320 : #endif
|