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