LCOV - code coverage report
Current view: top level - xpcom/ds - nsExpirationTracker.h (source / functions) Hit Total Coverage
Test: output.info Lines: 134 195 68.7 %
Date: 2017-07-14 16:53:18 Functions: 122 375 32.5 %
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             : #ifndef NSEXPIRATIONTRACKER_H_
       8             : #define NSEXPIRATIONTRACKER_H_
       9             : 
      10             : #include "mozilla/Logging.h"
      11             : #include "nsTArray.h"
      12             : #include "nsITimer.h"
      13             : #include "nsCOMPtr.h"
      14             : #include "nsAutoPtr.h"
      15             : #include "nsComponentManagerUtils.h"
      16             : #include "nsIEventTarget.h"
      17             : #include "nsIObserver.h"
      18             : #include "nsIObserverService.h"
      19             : #include "nsThreadUtils.h"
      20             : #include "mozilla/Attributes.h"
      21             : #include "mozilla/Services.h"
      22             : 
      23             : /**
      24             :  * Data used to track the expiration state of an object. We promise that this
      25             :  * is 32 bits so that objects that includes this as a field can pad and align
      26             :  * efficiently.
      27             :  */
      28             : struct nsExpirationState
      29             : {
      30             :   enum
      31             :   {
      32             :     NOT_TRACKED = (1U << 4) - 1,
      33             :     MAX_INDEX_IN_GENERATION = (1U << 28) - 1
      34             :   };
      35             : 
      36         216 :   nsExpirationState() : mGeneration(NOT_TRACKED) {}
      37         460 :   bool IsTracked() { return mGeneration != NOT_TRACKED; }
      38             : 
      39             :   /**
      40             :    * The generation that this object belongs to, or NOT_TRACKED.
      41             :    */
      42             :   uint32_t mGeneration:4;
      43             :   uint32_t mIndexInGeneration:28;
      44             : };
      45             : 
      46             : /**
      47             :  * ExpirationTracker classes:
      48             :  * - ExpirationTrackerImpl (Thread-safe class)
      49             :  * - nsExpirationTracker (Main-thread only class)
      50             :  *
      51             :  * These classes can track the lifetimes and usage of a large number of
      52             :  * objects, and send a notification some window of time after a live object was
      53             :  * last used. This is very useful when you manage a large number of objects
      54             :  * and want to flush some after they haven't been used for a while.
      55             :  * nsExpirationTracker is designed to be very space and time efficient.
      56             :  *
      57             :  * The type parameter T is the object type that we will track pointers to. T
      58             :  * must include an accessible method GetExpirationState() that returns a
      59             :  * pointer to an nsExpirationState associated with the object (preferably,
      60             :  * stored in a field of the object).
      61             :  *
      62             :  * The parameter K is the number of generations that will be used. Increasing
      63             :  * the number of generations narrows the window within which we promise
      64             :  * to fire notifications, at a slight increase in space cost for the tracker.
      65             :  * We require 2 <= K <= nsExpirationState::NOT_TRACKED (currently 15).
      66             :  *
      67             :  * To use this class, you need to inherit from it and override the
      68             :  * NotifyExpired() method.
      69             :  *
      70             :  * The approach is to track objects in K generations. When an object is accessed
      71             :  * it moves from its current generation to the newest generation. Generations
      72             :  * are stored in a cyclic array; when a timer interrupt fires, we advance
      73             :  * the current generation pointer to effectively age all objects very efficiently.
      74             :  * By storing information in each object about its generation and index within its
      75             :  * generation array, we make removal of objects from a generation very cheap.
      76             :  *
      77             :  * Future work:
      78             :  * -- Add a method to change the timer period?
      79             :  */
      80             : 
      81             : /**
      82             :  * Base class for ExiprationTracker implementations.
      83             :  *
      84             :  * nsExpirationTracker class below is a specialized class to be inherited by the
      85             :  * instances to be accessed only on main-thread.
      86             :  *
      87             :  * For creating a thread-safe tracker, you can define a subclass inheriting this
      88             :  * base class and specialize the Mutex and AutoLock to be used.
      89             :  *
      90             :  */
      91             : template<typename T, uint32_t K, typename Mutex, typename AutoLock>
      92             : class ExpirationTrackerImpl
      93             : {
      94             : public:
      95             :   /**
      96             :    * Initialize the tracker.
      97             :    * @param aTimerPeriod the timer period in milliseconds. The guarantees
      98             :    * provided by the tracker are defined in terms of this period. If the
      99             :    * period is zero, then we don't use a timer and rely on someone calling
     100             :    * AgeOneGenerationLocked explicitly.
     101             :    * @param aName the name of the subclass for telemetry.
     102             :    * @param aEventTarget the optional event target on main thread to label the
     103             :    * runnable of the asynchronous invocation to NotifyExpired().
     104             : 
     105             :    */
     106          19 :   ExpirationTrackerImpl(uint32_t aTimerPeriod,
     107             :                         const char* aName,
     108             :                         nsIEventTarget* aEventTarget = nullptr)
     109             :     : mTimerPeriod(aTimerPeriod)
     110             :     , mNewestGeneration(0)
     111             :     , mInAgeOneGeneration(false)
     112             :     , mName(aName)
     113          19 :     , mEventTarget(aEventTarget)
     114             :   {
     115             :     static_assert(K >= 2 && K <= nsExpirationState::NOT_TRACKED,
     116             :                   "Unsupported number of generations (must be 2 <= K <= 15)");
     117          19 :     MOZ_ASSERT(NS_IsMainThread());
     118          19 :     if (mEventTarget) {
     119          13 :       bool current = false;
     120             :       // NOTE: The following check+crash could be condensed into a
     121             :       // MOZ_RELEASE_ASSERT, but that triggers a segfault during compilation in
     122             :       // clang 3.8. Once we don't have to care about clang 3.8 anymore, though,
     123             :       // we can convert to MOZ_RELEASE_ASSERT here.
     124          13 :       if (MOZ_UNLIKELY(NS_FAILED(mEventTarget->IsOnCurrentThread(&current)) ||
     125             :                        !current)) {
     126           0 :         MOZ_CRASH("Provided event target must be on the main thread");
     127             :       }
     128             :     }
     129          19 :     mObserver = new ExpirationTrackerObserver();
     130          19 :     mObserver->Init(this);
     131          19 :   }
     132             : 
     133           0 :   virtual ~ExpirationTrackerImpl()
     134             :   {
     135           0 :     MOZ_ASSERT(NS_IsMainThread());
     136           0 :     if (mTimer) {
     137           0 :       mTimer->Cancel();
     138             :     }
     139           0 :     mObserver->Destroy();
     140           0 :   }
     141             : 
     142             :   /**
     143             :    * Add an object to be tracked. It must not already be tracked. It will
     144             :    * be added to the newest generation, i.e., as if it was just used.
     145             :    * @return an error on out-of-memory
     146             :    */
     147          98 :   nsresult AddObjectLocked(T* aObj, const AutoLock& aAutoLock)
     148             :   {
     149          98 :     nsExpirationState* state = aObj->GetExpirationState();
     150          98 :     MOZ_ASSERT(!state->IsTracked(),
     151             :                "Tried to add an object that's already tracked");
     152          98 :     nsTArray<T*>& generation = mGenerations[mNewestGeneration];
     153          98 :     uint32_t index = generation.Length();
     154          98 :     if (index > nsExpirationState::MAX_INDEX_IN_GENERATION) {
     155           0 :       NS_WARNING("More than 256M elements tracked, this is probably a problem");
     156           0 :       return NS_ERROR_OUT_OF_MEMORY;
     157             :     }
     158          98 :     if (index == 0) {
     159             :       // We might need to start the timer
     160          69 :       nsresult rv = CheckStartTimerLocked(aAutoLock);
     161          69 :       if (NS_FAILED(rv)) {
     162           0 :         return rv;
     163             :       }
     164             :     }
     165          98 :     if (!generation.AppendElement(aObj)) {
     166           0 :       return NS_ERROR_OUT_OF_MEMORY;
     167             :     }
     168          98 :     state->mGeneration = mNewestGeneration;
     169          98 :     state->mIndexInGeneration = index;
     170          98 :     return NS_OK;
     171             :   }
     172             : 
     173             :   /**
     174             :    * Remove an object from the tracker. It must currently be tracked.
     175             :    */
     176          75 :   void RemoveObjectLocked(T* aObj, const AutoLock& aAutoLock)
     177             :   {
     178          75 :     nsExpirationState* state = aObj->GetExpirationState();
     179          75 :     MOZ_ASSERT(state->IsTracked(), "Tried to remove an object that's not tracked");
     180          75 :     nsTArray<T*>& generation = mGenerations[state->mGeneration];
     181          75 :     uint32_t index = state->mIndexInGeneration;
     182          75 :     MOZ_ASSERT(generation.Length() > index &&
     183             :                generation[index] == aObj, "Object is lying about its index");
     184             :     // Move the last object to fill the hole created by removing aObj
     185          75 :     uint32_t last = generation.Length() - 1;
     186          75 :     T* lastObj = generation[last];
     187          75 :     generation[index] = lastObj;
     188          75 :     lastObj->GetExpirationState()->mIndexInGeneration = index;
     189          75 :     generation.RemoveElementAt(last);
     190          75 :     MOZ_ASSERT(generation.Length() == last);
     191          75 :     state->mGeneration = nsExpirationState::NOT_TRACKED;
     192             :     // We do not check whether we need to stop the timer here. The timer
     193             :     // will check that itself next time it fires. Checking here would not
     194             :     // be efficient since we'd need to track all generations. Also we could
     195             :     // thrash by incessantly creating and destroying timers if someone
     196             :     // kept adding and removing an object from the tracker.
     197          75 :   }
     198             : 
     199             :   /**
     200             :    * Notify that an object has been used.
     201             :    * @return an error if we lost the object from the tracker...
     202             :    */
     203          40 :   nsresult MarkUsedLocked(T* aObj, const AutoLock& aAutoLock)
     204             :   {
     205          40 :     nsExpirationState* state = aObj->GetExpirationState();
     206          40 :     if (mNewestGeneration == state->mGeneration) {
     207          35 :       return NS_OK;
     208             :     }
     209           5 :     RemoveObjectLocked(aObj, aAutoLock);
     210           5 :     return AddObjectLocked(aObj, aAutoLock);
     211             :   }
     212             : 
     213             :   /**
     214             :    * The timer calls this, but it can also be manually called if you want
     215             :    * to age objects "artifically". This can result in calls to NotifyExpiredLocked.
     216             :    */
     217          19 :   void AgeOneGenerationLocked(const AutoLock& aAutoLock)
     218             :   {
     219          19 :     if (mInAgeOneGeneration) {
     220           0 :       NS_WARNING("Can't reenter AgeOneGeneration from NotifyExpired");
     221           0 :       return;
     222             :     }
     223             : 
     224          19 :     mInAgeOneGeneration = true;
     225             :     uint32_t reapGeneration =
     226          19 :       mNewestGeneration > 0 ? mNewestGeneration - 1 : K - 1;
     227          19 :     nsTArray<T*>& generation = mGenerations[reapGeneration];
     228             :     // The following is rather tricky. We have to cope with objects being
     229             :     // removed from this generation either because of a call to RemoveObject
     230             :     // (or indirectly via MarkUsedLocked) inside NotifyExpiredLocked. Fortunately
     231             :     // no objects can be added to this generation because it's not the newest
     232             :     // generation. We depend on the fact that RemoveObject can only cause
     233             :     // the indexes of objects in this generation to *decrease*, not increase.
     234             :     // So if we start from the end and work our way backwards we are guaranteed
     235             :     // to see each object at least once.
     236          19 :     size_t index = generation.Length();
     237             :     for (;;) {
     238             :       // Objects could have been removed so index could be outside
     239             :       // the array
     240          33 :       index = XPCOM_MIN(index, generation.Length());
     241          26 :       if (index == 0) {
     242          19 :         break;
     243             :       }
     244           7 :       --index;
     245           7 :       NotifyExpiredLocked(generation[index], aAutoLock);
     246             :     }
     247             :     // Any leftover objects from reapGeneration just end up in the new
     248             :     // newest-generation. This is bad form, though, so warn if there are any.
     249          19 :     if (!generation.IsEmpty()) {
     250           0 :       NS_WARNING("Expired objects were not removed or marked used");
     251             :     }
     252             :     // Free excess memory used by the generation array, since we probably
     253             :     // just removed most or all of its elements.
     254          19 :     generation.Compact();
     255          19 :     mNewestGeneration = reapGeneration;
     256          19 :     mInAgeOneGeneration = false;
     257             :   }
     258             : 
     259             :   /**
     260             :    * This just calls AgeOneGenerationLocked K times. Under normal circumstances
     261             :    * this will result in all objects getting NotifyExpiredLocked called on them,
     262             :    * but if NotifyExpiredLocked itself marks some objects as used, then those
     263             :    * objects might not expire. This would be a good thing to call if we get into
     264             :    * a critically-low memory situation.
     265             :    */
     266           0 :   void AgeAllGenerationsLocked(const AutoLock& aAutoLock)
     267             :   {
     268             :     uint32_t i;
     269           0 :     for (i = 0; i < K; ++i) {
     270           0 :       AgeOneGenerationLocked(aAutoLock);
     271             :     }
     272           0 :   }
     273             : 
     274             :   class Iterator
     275             :   {
     276             :   private:
     277             :     ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mTracker;
     278             :     uint32_t mGeneration;
     279             :     uint32_t mIndex;
     280             :   public:
     281          86 :     Iterator(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aTracker,
     282             :              AutoLock& aAutoLock)
     283             :       : mTracker(aTracker)
     284             :       , mGeneration(0)
     285          86 :       , mIndex(0)
     286             :     {
     287          86 :     }
     288             : 
     289         385 :     T* Next()
     290             :     {
     291         643 :       while (mGeneration < K) {
     292         299 :         nsTArray<T*>* generation = &mTracker->mGenerations[mGeneration];
     293         299 :         if (mIndex < generation->Length()) {
     294          41 :           ++mIndex;
     295          41 :           return (*generation)[mIndex - 1];
     296             :         }
     297         258 :         ++mGeneration;
     298         258 :         mIndex = 0;
     299             :       }
     300          86 :       return nullptr;
     301             :     }
     302             :   };
     303             : 
     304             :   friend class Iterator;
     305             : 
     306          19 :   bool IsEmptyLocked(const AutoLock& aAutoLock)
     307             :   {
     308          46 :     for (uint32_t i = 0; i < K; ++i) {
     309          45 :       if (!mGenerations[i].IsEmpty()) {
     310          18 :         return false;
     311             :       }
     312             :     }
     313           1 :     return true;
     314             :   }
     315             : 
     316             : protected:
     317             :   /**
     318             :    * This must be overridden to catch notifications. It is called whenever
     319             :    * we detect that an object has not been used for at least (K-1)*mTimerPeriod
     320             :    * milliseconds. If timer events are not delayed, it will be called within
     321             :    * roughly K*mTimerPeriod milliseconds after the last use.
     322             :    * (Unless AgeOneGenerationLocked or AgeAllGenerationsLocked have been called
     323             :    * to accelerate the aging process.)
     324             :    *
     325             :    * NOTE: These bounds ignore delays in timer firings due to actual work being
     326             :    * performed by the browser. We use a slack timer so there is always at least
     327             :    * mTimerPeriod milliseconds between firings, which gives us (K-1)*mTimerPeriod
     328             :    * as a pretty solid lower bound. The upper bound is rather loose, however.
     329             :    * If the maximum amount by which any given timer firing is delayed is D, then
     330             :    * the upper bound before NotifyExpiredLocked is called is K*(mTimerPeriod + D).
     331             :    *
     332             :    * The NotifyExpiredLocked call is expected to remove the object from the tracker,
     333             :    * but it need not. The object (or other objects) could be "resurrected"
     334             :    * by calling MarkUsedLocked() on them, or they might just not be removed.
     335             :    * Any objects left over that have not been resurrected or removed
     336             :    * are placed in the new newest-generation, but this is considered "bad form"
     337             :    * and should be avoided (we'll issue a warning). (This recycling counts
     338             :    * as "a use" for the purposes of the expiry guarantee above...)
     339             :    *
     340             :    * For robustness and simplicity, we allow objects to be notified more than
     341             :    * once here in the same timer tick.
     342             :    */
     343             :   virtual void NotifyExpiredLocked(T*, const AutoLock&) = 0;
     344             : 
     345             :   virtual Mutex& GetMutex() = 0;
     346             : 
     347             : private:
     348             :   class ExpirationTrackerObserver;
     349             :   RefPtr<ExpirationTrackerObserver> mObserver;
     350             :   nsTArray<T*>       mGenerations[K];
     351             :   nsCOMPtr<nsITimer> mTimer;
     352             :   uint32_t           mTimerPeriod;
     353             :   uint32_t           mNewestGeneration;
     354             :   bool               mInAgeOneGeneration;
     355             :   const char* const  mName;   // Used for timer firing profiling.
     356             :   const nsCOMPtr<nsIEventTarget> mEventTarget;
     357             : 
     358             :   /**
     359             :    * Whenever "memory-pressure" is observed, it calls AgeAllGenerationsLocked()
     360             :    * to minimize memory usage.
     361             :    */
     362          19 :   class ExpirationTrackerObserver final : public nsIObserver
     363             :   {
     364             :   public:
     365          19 :     void Init(ExpirationTrackerImpl<T, K, Mutex, AutoLock>* aObj)
     366             :     {
     367          19 :       mOwner = aObj;
     368          38 :       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     369          19 :       if (obs) {
     370          19 :         obs->AddObserver(this, "memory-pressure", false);
     371             :       }
     372          19 :     }
     373           0 :     void Destroy()
     374             :     {
     375           0 :       mOwner = nullptr;
     376           0 :       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     377           0 :       if (obs) {
     378           0 :         obs->RemoveObserver(this, "memory-pressure");
     379             :       }
     380           0 :     }
     381             :     NS_DECL_ISUPPORTS
     382             :     NS_DECL_NSIOBSERVER
     383             :   private:
     384             :     ExpirationTrackerImpl<T, K, Mutex, AutoLock>* mOwner;
     385             :   };
     386             : 
     387           0 :   void HandleLowMemory() {
     388           0 :     AutoLock lock(GetMutex());
     389           0 :     AgeAllGenerationsLocked(lock);
     390           0 :   }
     391             : 
     392          19 :   void HandleTimeout() {
     393          19 :     AutoLock lock(GetMutex());
     394          19 :     AgeOneGenerationLocked(lock);
     395             :     // Cancel the timer if we have no objects to track
     396          19 :     if (IsEmptyLocked(lock)) {
     397           1 :       mTimer->Cancel();
     398           1 :       mTimer = nullptr;
     399             :     }
     400          19 :   }
     401             : 
     402          19 :   static void TimerCallback(nsITimer* aTimer, void* aThis)
     403             :   {
     404          19 :     ExpirationTrackerImpl* tracker = static_cast<ExpirationTrackerImpl*>(aThis);
     405          19 :     tracker->HandleTimeout();
     406          19 :   }
     407             : 
     408          69 :   nsresult CheckStartTimerLocked(const AutoLock& aAutoLock)
     409             :   {
     410          69 :     if (mTimer || !mTimerPeriod) {
     411          57 :       return NS_OK;
     412             :     }
     413          12 :     mTimer = do_CreateInstance("@mozilla.org/timer;1");
     414          12 :     if (!mTimer) {
     415           0 :       return NS_ERROR_OUT_OF_MEMORY;
     416             :     }
     417          12 :     if (mEventTarget) {
     418          10 :       mTimer->SetTarget(mEventTarget);
     419           2 :     } else if (!NS_IsMainThread()) {
     420             :       // TimerCallback should always be run on the main thread to prevent races
     421             :       // to the destruction of the tracker.
     422           0 :       nsCOMPtr<nsIEventTarget> target = do_GetMainThread();
     423           0 :       NS_ENSURE_STATE(target);
     424           0 :       mTimer->SetTarget(target);
     425             :     }
     426          24 :     mTimer->InitWithNamedFuncCallback(
     427             :       TimerCallback,
     428             :       this,
     429             :       mTimerPeriod,
     430             :       nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
     431          12 :       mName);
     432          12 :     return NS_OK;
     433             :   }
     434             : };
     435             : 
     436             : namespace detail {
     437             : 
     438             : class PlaceholderLock {
     439             : public:
     440             :   void Lock() {}
     441             :   void Unlock() {}
     442             : };
     443             : 
     444             : class PlaceholderAutoLock {
     445             : public:
     446         280 :   explicit PlaceholderAutoLock(PlaceholderLock&) { }
     447             :   ~PlaceholderAutoLock() = default;
     448             : 
     449             : };
     450             : 
     451             : template<typename T, uint32_t K>
     452             : using SingleThreadedExpirationTracker =
     453             :   ExpirationTrackerImpl<T, K, PlaceholderLock, PlaceholderAutoLock>;
     454             : 
     455             : } // namespace detail
     456             : 
     457             : template<typename T, uint32_t K>
     458             : class nsExpirationTracker : protected ::detail::SingleThreadedExpirationTracker<T, K>
     459             : {
     460             :   typedef ::detail::PlaceholderLock Lock;
     461             :   typedef ::detail::PlaceholderAutoLock AutoLock;
     462             : 
     463             :   Lock mLock;
     464             : 
     465         175 :   AutoLock FakeLock() {
     466         175 :     return AutoLock(mLock);
     467             :   }
     468             : 
     469         105 :   Lock& GetMutex() override
     470             :   {
     471         105 :     return mLock;
     472             :   }
     473             : 
     474           7 :   void NotifyExpiredLocked(T* aObject, const AutoLock&) override
     475             :   {
     476           7 :     NotifyExpired(aObject);
     477           7 :   }
     478             : 
     479             : protected:
     480             :   virtual void NotifyExpired(T* aObj) = 0;
     481             : 
     482             : public:
     483          16 :   nsExpirationTracker(uint32_t aTimerPeriod,
     484             :                       const char* aName,
     485             :                       nsIEventTarget* aEventTarget = nullptr)
     486             :     : ::detail::SingleThreadedExpirationTracker<T, K>(aTimerPeriod,
     487             :                                                       aName,
     488          16 :                                                       aEventTarget)
     489          16 :   { }
     490             : 
     491           0 :   virtual ~nsExpirationTracker()
     492           0 :   { }
     493             : 
     494          79 :   nsresult AddObject(T* aObj)
     495             :   {
     496          79 :     return this->AddObjectLocked(aObj, FakeLock());
     497             :   }
     498             : 
     499          56 :   void RemoveObject(T* aObj)
     500             :   {
     501          56 :     this->RemoveObjectLocked(aObj, FakeLock());
     502          56 :   }
     503             : 
     504          40 :   nsresult MarkUsed(T* aObj)
     505             :   {
     506          40 :     return this->MarkUsedLocked(aObj, FakeLock());
     507             :   }
     508             : 
     509           0 :   void AgeOneGeneration()
     510             :   {
     511           0 :     this->AgeOneGenerationLocked(FakeLock());
     512           0 :   }
     513             : 
     514           0 :   void AgeAllGenerations()
     515             :   {
     516           0 :     this->AgeAllGenerationsLocked(FakeLock());
     517           0 :   }
     518             : 
     519             :   class Iterator
     520             :   {
     521             :   private:
     522             :     AutoLock mAutoLock;
     523             :     typename ExpirationTrackerImpl<T, K, Lock, AutoLock>::Iterator mIterator;
     524             :   public:
     525          86 :     explicit Iterator(nsExpirationTracker<T, K>* aTracker)
     526             :       : mAutoLock(aTracker->GetMutex())
     527          86 :       , mIterator(aTracker, mAutoLock)
     528             :     {
     529          86 :     }
     530             : 
     531         127 :     T* Next()
     532             :     {
     533         127 :       return mIterator.Next();
     534             :     }
     535             :   };
     536             : 
     537             :   friend class Iterator;
     538             : 
     539           0 :   bool IsEmpty()
     540             :   {
     541           0 :     return this->IsEmptyLocked(FakeLock());
     542             :   }
     543             : };
     544             : 
     545             : template<typename T, uint32_t K, typename Mutex, typename AutoLock>
     546             : NS_IMETHODIMP
     547           0 : ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
     548             : ExpirationTrackerObserver::Observe(
     549             :     nsISupports* aSubject, const char* aTopic, const char16_t* aData)
     550             : {
     551           0 :   if (!strcmp(aTopic, "memory-pressure") && mOwner) {
     552           0 :     mOwner->HandleLowMemory();
     553             :   }
     554           0 :   return NS_OK;
     555             : }
     556             : 
     557             : template<class T, uint32_t K, typename Mutex, typename AutoLock>
     558             : NS_IMETHODIMP_(MozExternalRefCountType)
     559          38 : ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
     560             : ExpirationTrackerObserver::AddRef(void)
     561             : {
     562          38 :   MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
     563          38 :   NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
     564          38 :   ++mRefCnt;
     565          38 :   NS_LOG_ADDREF(this, mRefCnt, "ExpirationTrackerObserver", sizeof(*this));
     566          38 :   return mRefCnt;
     567             : }
     568             : 
     569             : template<class T, uint32_t K, typename Mutex, typename AutoLock>
     570             : NS_IMETHODIMP_(MozExternalRefCountType)
     571           0 : ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
     572             : ExpirationTrackerObserver::Release(void)
     573             : {
     574           0 :   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
     575           0 :   NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
     576           0 :   --mRefCnt;
     577           0 :   NS_LOG_RELEASE(this, mRefCnt, "ExpirationTrackerObserver");
     578           0 :   if (mRefCnt == 0) {
     579           0 :     NS_ASSERT_OWNINGTHREAD(ExpirationTrackerObserver);
     580           0 :     mRefCnt = 1; /* stabilize */
     581             :     delete (this);
     582           0 :     return 0;
     583             :   }
     584           0 :   return mRefCnt;
     585             : }
     586             : 
     587             : template<class T, uint32_t K, typename Mutex, typename AutoLock>
     588             : NS_IMETHODIMP
     589           0 : ExpirationTrackerImpl<T, K, Mutex, AutoLock>::
     590             : ExpirationTrackerObserver::QueryInterface(
     591             :     REFNSIID aIID, void** aInstancePtr)
     592             : {
     593           0 :   NS_ASSERTION(aInstancePtr,
     594             :                "QueryInterface requires a non-NULL destination!");
     595           0 :   nsresult rv = NS_ERROR_FAILURE;
     596           0 :   NS_INTERFACE_TABLE(ExpirationTrackerObserver, nsIObserver)
     597           0 :   return rv;
     598             : }
     599             : 
     600             : #endif /*NSEXPIRATIONTRACKER_H_*/

Generated by: LCOV version 1.13