bug 708075 - observe memory-pressure notification and discard cached gfxShapedWord records. r=roc
☠☠ backed out by 76fc8cdd2a31 ☠ ☠
authorJonathan Kew <jfkthame@gmail.com>
Thu, 12 Jan 2012 12:44:05 +0000
changeset 84341 f94f83c7f2dfd510d5a68937d4263f999355b970
parent 84340 6bf51f18e69e8d8c2279382daa11a1dcc67c3e23
child 84342 76fc8cdd2a31b8d0365fdfdac6a50f0db0906391
push id21842
push usermak77@bonardo.net
push dateFri, 13 Jan 2012 08:56:37 +0000
treeherdermozilla-central@8d4638feec54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs708075
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 708075 - observe memory-pressure notification and discard cached gfxShapedWord records. r=roc
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -64,28 +64,30 @@
 #include "gfxScriptItemizer.h"
 #include "gfxUnicodeProperties.h"
 #include "nsMathUtils.h"
 #include "nsBidiUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsCompressedCharMap.h"
 #include "nsStyleConsts.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
 
 #include "cairo.h"
 #include "gfxFontTest.h"
 
 #include "harfbuzz/hb-blob.h"
 
 #include "nsCRT.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using mozilla::services::GetObserverService;
 
 gfxFontCache *gfxFontCache::gGlobalCache = nsnull;
 
 #ifdef DEBUG_roc
 #define DEBUG_TEXT_RUN_STORAGE_METRICS
 #endif
 
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
@@ -949,16 +951,25 @@ gfxFontFamily::FindFont(const nsAString&
     for (PRUint32 i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i].get();
         if (fe && fe->Name() == aPostscriptName)
             return fe;
     }
     return nsnull;
 }
 
+/*
+ * gfxFontCache - global cache of gfxFont instances.
+ * Expires unused fonts after a short interval;
+ * notifies fonts to age their cached shaped-word records;
+ * observes memory-pressure notification and tells fonts to clear their
+ * shaped-word caches to free up memory.
+ */
+
+NS_IMPL_ISUPPORTS1(gfxFontCache, nsIObserver)
 
 nsresult
 gfxFontCache::Init()
 {
     NS_ASSERTION(!gGlobalCache, "Where did this come from?");
     gGlobalCache = new gfxFontCache();
     return gGlobalCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
@@ -981,32 +992,43 @@ gfxFontCache::Shutdown()
     printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
 #endif
 }
 
 gfxFontCache::gfxFontCache()
     : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
 {
     mFonts.Init();
+
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (obs) {
+        obs->AddObserver(this, "memory-pressure", false);
+    }
+
     mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (mWordCacheExpirationTimer) {
         mWordCacheExpirationTimer->
             InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
                                  SHAPED_WORD_TIMEOUT_SECONDS * 1000,
                                  nsITimer::TYPE_REPEATING_SLACK);
     }
 }
 
 gfxFontCache::~gfxFontCache()
 {
     if (mWordCacheExpirationTimer) {
         mWordCacheExpirationTimer->Cancel();
         mWordCacheExpirationTimer = nsnull;
     }
 
+    nsCOMPtr<nsIObserverService> obs = GetObserverService();
+    if (obs) {
+        obs->RemoveObserver(this, "memory-pressure");
+    }
+
     // Expire everything that has a zero refcount, so we don't leak them.
     AgeAllGenerations();
     // All fonts should be gone.
     NS_WARN_IF_FALSE(mFonts.Count() == 0,
                      "Fonts still alive while shutting down gfxFontCache");
     // Note that we have to delete everything through the expiration
     // tracker, since there might be fonts not in the hashtable but in
     // the tracker.
@@ -1097,16 +1119,33 @@ gfxFontCache::AgeCachedWordsForFont(Hash
 /*static*/
 void
 gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
 {
     gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
     cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nsnull);
 }
 
+NS_IMETHODIMP
+gfxFontCache::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
+{
+    if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
+        mFonts.EnumerateEntries(ClearCachedWordsForFont, nsnull);
+    }
+    return NS_OK;
+}
+
+/*static*/
+PLDHashOperator
+gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
+{
+    aHashEntry->mFont->ClearCachedWords();
+    return PL_DHASH_NEXT;
+}
+
 void
 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
 {
     mAscent = NS_MAX(mAscent, aOther.mAscent);
     mDescent = NS_MAX(mDescent, aOther.mDescent);
     if (aOtherIsOnLeft) {
         mBoundingBox =
             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -52,16 +52,17 @@
 #include "nsHashKeys.h"
 #include "gfxSkipChars.h"
 #include "gfxRect.h"
 #include "nsExpirationTracker.h"
 #include "gfxFontConstants.h"
 #include "gfxPlatform.h"
 #include "nsIAtom.h"
 #include "nsISupportsImpl.h"
+#include "nsIObserver.h"
 
 typedef struct _cairo_scaled_font cairo_scaled_font_t;
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 class gfxContext;
@@ -678,18 +679,24 @@ struct gfxTextRange {
  * by the fonts if they get aged three times without being re-used in the
  * meantime.
  *
  * Note that the ShapedWord timeout is much larger than the font timeout,
  * so that in the case of a short-lived font, we'll discard the gfxFont
  * completely, with all its words, and avoid the cost of aging the words
  * individually. That only happens with longer-lived fonts.
  */
-class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
+class THEBES_API gfxFontCache
+    : public nsExpirationTracker<gfxFont,3>
+    , public nsIObserver
+{
 public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+
     enum {
         FONT_TIMEOUT_SECONDS = 10,
         SHAPED_WORD_TIMEOUT_SECONDS = 60
     };
 
     gfxFontCache();
     ~gfxFontCache();
 
@@ -762,16 +769,18 @@ protected:
         }
         enum { ALLOW_MEMMOVE = true };
 
         gfxFont* mFont;
     };
 
     nsTHashtable<HashEntry> mFonts;
 
+    static PLDHashOperator ClearCachedWordsForFont(HashEntry* aHashEntry, void*);
+
     static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*);
     static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
     nsCOMPtr<nsITimer>      mWordCacheExpirationTimer;
 };
 
 class THEBES_API gfxTextRunFactory {
     NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
 
@@ -1400,16 +1409,23 @@ public:
     // Called by the gfxFontCache timer to increment the age of all the words,
     // so that they'll expire after a sufficient period of non-use
     void AgeCachedWords() {
         if (mWordCache.IsInitialized()) {
             (void)mWordCache.EnumerateEntries(AgeCacheEntry, this);
         }
     }
 
+    // Discard all cached word records; called on memory-pressure notification.
+    void ClearCachedWords() {
+        if (mWordCache.IsInitialized()) {
+            mWordCache.Clear();
+        }
+    }
+
 protected:
     // Call the appropriate shaper to generate glyphs for aText and store
     // them into aShapedWord.
     // The length of the text is aShapedWord->Length().
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aText,
                            bool aPreferPlatformShaping = false);