LCOV - code coverage report
Current view: top level - xpcom/threads - StateWatching.h (source / functions) Hit Total Coverage
Test: output.info Lines: 32 102 31.4 %
Date: 2017-07-14 16:53:18 Functions: 12 125 9.6 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13