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(StateMirroring_h_)
8 : #define StateMirroring_h_
9 :
10 : #include "mozilla/Maybe.h"
11 : #include "mozilla/MozPromise.h"
12 : #include "mozilla/StateWatching.h"
13 : #include "mozilla/TaskDispatcher.h"
14 : #include "mozilla/UniquePtr.h"
15 : #include "mozilla/Unused.h"
16 :
17 : #include "mozilla/Logging.h"
18 : #include "nsISupportsImpl.h"
19 :
20 : /*
21 : * The state-mirroring machinery allows pieces of interesting state to be
22 : * observed on multiple thread without locking. The basic strategy is to track
23 : * changes in a canonical value and post updates to other threads that hold
24 : * mirrors for that value.
25 : *
26 : * One problem with the naive implementation of such a system is that some pieces
27 : * of state need to be updated atomically, and certain other operations need to
28 : * wait for these atomic updates to complete before executing. The state-mirroring
29 : * machinery solves this problem by requiring that its owner thread uses tail
30 : * dispatch, and posting state update events (which should always be run first by
31 : * TaskDispatcher implementations) to that tail dispatcher. This ensures that
32 : * state changes are always atomic from the perspective of observing threads.
33 : *
34 : * Given that state-mirroring is an automatic background process, we try to avoid
35 : * burdening the caller with worrying too much about teardown. To that end, we
36 : * don't assert dispatch success for any of the notifications, and assume that
37 : * any canonical or mirror owned by a thread for whom dispatch fails will soon
38 : * be disconnected by its holder anyway.
39 : *
40 : * Given that semantics may change and comments tend to go out of date, we
41 : * deliberately don't provide usage examples here. Grep around to find them.
42 : */
43 :
44 : namespace mozilla {
45 :
46 : // Mirror<T> and Canonical<T> inherit WatchTarget, so we piggy-back on the
47 : // logging that WatchTarget already does. Given that, it makes sense to share
48 : // the same log module.
49 : #define MIRROR_LOG(x, ...) \
50 : MOZ_ASSERT(gStateWatchingLog); \
51 : MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
52 :
53 : template<typename T> class AbstractMirror;
54 :
55 : /*
56 : * AbstractCanonical is a superclass from which all Canonical values must
57 : * inherit. It serves as the interface of operations which may be performed (via
58 : * asynchronous dispatch) by other threads, in particular by the corresponding
59 : * Mirror value.
60 : */
61 : template<typename T>
62 : class AbstractCanonical
63 : {
64 : public:
65 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractCanonical)
66 0 : AbstractCanonical(AbstractThread* aThread) : mOwnerThread(aThread) {}
67 : virtual void AddMirror(AbstractMirror<T>* aMirror) = 0;
68 : virtual void RemoveMirror(AbstractMirror<T>* aMirror) = 0;
69 :
70 0 : AbstractThread* OwnerThread() const { return mOwnerThread; }
71 : protected:
72 0 : virtual ~AbstractCanonical() {}
73 : RefPtr<AbstractThread> mOwnerThread;
74 : };
75 :
76 : /*
77 : * AbstractMirror is a superclass from which all Mirror values must
78 : * inherit. It serves as the interface of operations which may be performed (via
79 : * asynchronous dispatch) by other threads, in particular by the corresponding
80 : * Canonical value.
81 : */
82 : template<typename T>
83 : class AbstractMirror
84 : {
85 : public:
86 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractMirror)
87 0 : AbstractMirror(AbstractThread* aThread) : mOwnerThread(aThread) {}
88 : virtual void UpdateValue(const T& aNewValue) = 0;
89 : virtual void NotifyDisconnected() = 0;
90 :
91 0 : AbstractThread* OwnerThread() const { return mOwnerThread; }
92 : protected:
93 0 : virtual ~AbstractMirror() {}
94 : RefPtr<AbstractThread> mOwnerThread;
95 : };
96 :
97 : /*
98 : * Canonical<T> is a wrapper class that allows a given value to be mirrored by other
99 : * threads. It maintains a list of active mirrors, and queues updates for them
100 : * when the internal value changes. When changing the value, the caller needs to
101 : * pass a TaskDispatcher object, which fires the updates at the appropriate time.
102 : * Canonical<T> is also a WatchTarget, and may be set up to trigger other routines
103 : * (on the same thread) when the canonical value changes.
104 : *
105 : * Canonical<T> is intended to be used as a member variable, so it doesn't actually
106 : * inherit AbstractCanonical<T> (a refcounted type). Rather, it contains an inner
107 : * class called |Impl| that implements most of the interesting logic.
108 : */
109 : template<typename T>
110 : class Canonical
111 : {
112 : public:
113 0 : Canonical(AbstractThread* aThread, const T& aInitialValue, const char* aName)
114 0 : {
115 0 : mImpl = new Impl(aThread, aInitialValue, aName);
116 0 : }
117 :
118 :
119 0 : ~Canonical() {}
120 :
121 : private:
122 : class Impl : public AbstractCanonical<T>, public WatchTarget
123 : {
124 : public:
125 : using AbstractCanonical<T>::OwnerThread;
126 :
127 0 : Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
128 0 : : AbstractCanonical<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
129 : {
130 0 : MIRROR_LOG("%s [%p] initialized", mName, this);
131 0 : MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
132 0 : }
133 :
134 0 : void AddMirror(AbstractMirror<T>* aMirror) override
135 : {
136 0 : MIRROR_LOG("%s [%p] adding mirror %p", mName, this, aMirror);
137 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
138 0 : MOZ_ASSERT(!mMirrors.Contains(aMirror));
139 0 : mMirrors.AppendElement(aMirror);
140 0 : aMirror->OwnerThread()->DispatchStateChange(MakeNotifier(aMirror));
141 0 : }
142 :
143 0 : void RemoveMirror(AbstractMirror<T>* aMirror) override
144 : {
145 0 : MIRROR_LOG("%s [%p] removing mirror %p", mName, this, aMirror);
146 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
147 0 : MOZ_ASSERT(mMirrors.Contains(aMirror));
148 0 : mMirrors.RemoveElement(aMirror);
149 0 : }
150 :
151 0 : void DisconnectAll()
152 : {
153 0 : MIRROR_LOG("%s [%p] Disconnecting all mirrors", mName, this);
154 0 : for (size_t i = 0; i < mMirrors.Length(); ++i) {
155 0 : mMirrors[i]->OwnerThread()->Dispatch(
156 : NewRunnableMethod("AbstractMirror::NotifyDisconnected",
157 0 : mMirrors[i],
158 : &AbstractMirror<T>::NotifyDisconnected),
159 : AbstractThread::DontAssertDispatchSuccess);
160 : }
161 0 : mMirrors.Clear();
162 0 : }
163 :
164 0 : operator const T&()
165 : {
166 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
167 0 : return mValue;
168 : }
169 :
170 0 : void Set(const T& aNewValue)
171 : {
172 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
173 :
174 0 : if (aNewValue == mValue) {
175 0 : return;
176 : }
177 :
178 : // Notify same-thread watchers. The state watching machinery will make sure
179 : // that notifications run at the right time.
180 0 : NotifyWatchers();
181 :
182 : // Check if we've already got a pending update. If so we won't schedule another
183 : // one.
184 0 : bool alreadyNotifying = mInitialValue.isSome();
185 :
186 : // Stash the initial value if needed, then update to the new value.
187 0 : if (mInitialValue.isNothing()) {
188 0 : mInitialValue.emplace(mValue);
189 : }
190 0 : mValue = aNewValue;
191 :
192 : // We wait until things have stablized before sending state updates so that
193 : // we can avoid sending multiple updates, and possibly avoid sending any
194 : // updates at all if the value ends up where it started.
195 0 : if (!alreadyNotifying) {
196 0 : AbstractThread::DispatchDirectTask(NewRunnableMethod(
197 : "Canonical::Impl::DoNotify", this, &Impl::DoNotify));
198 : }
199 : }
200 :
201 : Impl& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
202 : Impl& operator=(const Impl& aOther) { Set(aOther); return *this; }
203 : Impl(const Impl& aOther) = delete;
204 :
205 : protected:
206 0 : ~Impl() { MOZ_DIAGNOSTIC_ASSERT(mMirrors.IsEmpty()); }
207 :
208 : private:
209 0 : void DoNotify()
210 : {
211 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
212 0 : MOZ_ASSERT(mInitialValue.isSome());
213 0 : bool same = mInitialValue.ref() == mValue;
214 0 : mInitialValue.reset();
215 :
216 0 : if (same) {
217 0 : MIRROR_LOG("%s [%p] unchanged - not sending update", mName, this);
218 0 : return;
219 : }
220 :
221 0 : for (size_t i = 0; i < mMirrors.Length(); ++i) {
222 0 : mMirrors[i]->OwnerThread()->DispatchStateChange(MakeNotifier(mMirrors[i]));
223 : }
224 : }
225 :
226 0 : already_AddRefed<nsIRunnable> MakeNotifier(AbstractMirror<T>* aMirror)
227 : {
228 : return NewRunnableMethod<T>("AbstractMirror::UpdateValue",
229 : aMirror,
230 : &AbstractMirror<T>::UpdateValue,
231 0 : mValue);
232 : ;
233 : }
234 :
235 : T mValue;
236 : Maybe<T> mInitialValue;
237 : nsTArray<RefPtr<AbstractMirror<T>>> mMirrors;
238 : };
239 : public:
240 :
241 : // NB: Because mirror-initiated disconnection can race with canonical-
242 : // initiated disconnection, a canonical should never be reinitialized.
243 : // Forward control operations to the Impl.
244 0 : void DisconnectAll() { return mImpl->DisconnectAll(); }
245 :
246 : // Access to the Impl.
247 0 : operator Impl&() { return *mImpl; }
248 0 : Impl* operator&() { return mImpl; }
249 :
250 : // Access to the T.
251 0 : const T& Ref() const { return *mImpl; }
252 0 : operator const T&() const { return Ref(); }
253 0 : void Set(const T& aNewValue) { mImpl->Set(aNewValue); }
254 0 : Canonical& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
255 : Canonical& operator=(const Canonical& aOther) { Set(aOther); return *this; }
256 : Canonical(const Canonical& aOther) = delete;
257 :
258 : private:
259 : RefPtr<Impl> mImpl;
260 : };
261 :
262 : /*
263 : * Mirror<T> is a wrapper class that allows a given value to mirror that of a
264 : * Canonical<T> owned by another thread. It registers itself with a Canonical<T>,
265 : * and is periodically updated with new values. Mirror<T> is also a WatchTarget,
266 : * and may be set up to trigger other routines (on the same thread) when the
267 : * mirrored value changes.
268 : *
269 : * Mirror<T> is intended to be used as a member variable, so it doesn't actually
270 : * inherit AbstractMirror<T> (a refcounted type). Rather, it contains an inner
271 : * class called |Impl| that implements most of the interesting logic.
272 : */
273 : template<typename T>
274 : class Mirror
275 : {
276 : public:
277 0 : Mirror(AbstractThread* aThread, const T& aInitialValue, const char* aName)
278 0 : {
279 0 : mImpl = new Impl(aThread, aInitialValue, aName);
280 0 : }
281 :
282 0 : ~Mirror()
283 : {
284 : // As a member of complex objects, a Mirror<T> may be destroyed on a
285 : // different thread than its owner, or late in shutdown during CC. Given
286 : // that, we require manual disconnection so that callers can put things in
287 : // the right place.
288 0 : MOZ_DIAGNOSTIC_ASSERT(!mImpl->IsConnected());
289 0 : }
290 :
291 : private:
292 : class Impl : public AbstractMirror<T>, public WatchTarget
293 : {
294 : public:
295 : using AbstractMirror<T>::OwnerThread;
296 :
297 0 : Impl(AbstractThread* aThread, const T& aInitialValue, const char* aName)
298 0 : : AbstractMirror<T>(aThread), WatchTarget(aName), mValue(aInitialValue)
299 : {
300 0 : MIRROR_LOG("%s [%p] initialized", mName, this);
301 0 : MOZ_ASSERT(aThread->SupportsTailDispatch(), "Can't get coherency without tail dispatch");
302 0 : }
303 :
304 0 : operator const T&()
305 : {
306 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
307 0 : return mValue;
308 : }
309 :
310 0 : virtual void UpdateValue(const T& aNewValue) override
311 : {
312 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
313 0 : if (mValue != aNewValue) {
314 0 : mValue = aNewValue;
315 0 : WatchTarget::NotifyWatchers();
316 : }
317 0 : }
318 :
319 0 : virtual void NotifyDisconnected() override
320 : {
321 0 : MIRROR_LOG("%s [%p] Notifed of disconnection from %p", mName, this, mCanonical.get());
322 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
323 0 : mCanonical = nullptr;
324 0 : }
325 :
326 0 : bool IsConnected() const { return !!mCanonical; }
327 :
328 0 : void Connect(AbstractCanonical<T>* aCanonical)
329 : {
330 0 : MIRROR_LOG("%s [%p] Connecting to %p", mName, this, aCanonical);
331 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
332 0 : MOZ_ASSERT(!IsConnected());
333 0 : MOZ_ASSERT(OwnerThread()->RequiresTailDispatch(aCanonical->OwnerThread()), "Can't get coherency without tail dispatch");
334 :
335 : nsCOMPtr<nsIRunnable> r =
336 : NewRunnableMethod<StoreRefPtrPassByPtr<AbstractMirror<T>>>(
337 : "AbstractCanonical::AddMirror",
338 : aCanonical,
339 : &AbstractCanonical<T>::AddMirror,
340 0 : this);
341 0 : aCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
342 0 : mCanonical = aCanonical;
343 0 : }
344 : public:
345 :
346 0 : void DisconnectIfConnected()
347 : {
348 0 : MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn());
349 0 : if (!IsConnected()) {
350 0 : return;
351 : }
352 :
353 0 : MIRROR_LOG("%s [%p] Disconnecting from %p", mName, this, mCanonical.get());
354 : nsCOMPtr<nsIRunnable> r =
355 : NewRunnableMethod<StoreRefPtrPassByPtr<AbstractMirror<T>>>(
356 : "AbstractCanonical::RemoveMirror",
357 : mCanonical,
358 : &AbstractCanonical<T>::RemoveMirror,
359 0 : this);
360 0 : mCanonical->OwnerThread()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
361 0 : mCanonical = nullptr;
362 : }
363 :
364 : protected:
365 0 : ~Impl() { MOZ_DIAGNOSTIC_ASSERT(!IsConnected()); }
366 :
367 : private:
368 : T mValue;
369 : RefPtr<AbstractCanonical<T>> mCanonical;
370 : };
371 : public:
372 :
373 : // Forward control operations to the Impl<T>.
374 0 : void Connect(AbstractCanonical<T>* aCanonical) { mImpl->Connect(aCanonical); }
375 0 : void DisconnectIfConnected() { mImpl->DisconnectIfConnected(); }
376 :
377 : // Access to the Impl<T>.
378 0 : operator Impl&() { return *mImpl; }
379 : Impl* operator&() { return mImpl; }
380 :
381 : // Access to the T.
382 0 : const T& Ref() const { return *mImpl; }
383 0 : operator const T&() const { return Ref(); }
384 :
385 : private:
386 : RefPtr<Impl> mImpl;
387 : };
388 :
389 : #undef MIRROR_LOG
390 :
391 : } // namespace mozilla
392 :
393 : #endif
|