Bug 1371882 - Avoid MemoryBlockCache when combined sizes > 'media.memory_caches_combined_limit_...' - r?cpearce draft
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 12 Jun 2017 15:42:26 +1200
changeset 595163 eed65eafbabe59ca7c02f2c2c43ddd8ff349e99b
parent 595162 e117063a8b99f6ca7cde6655569c70d51f98eea6
child 595164 17aaf07adeb9a7468c6aeea101b1a53f1a43235e
push id64265
push usergsquelart@mozilla.com
push dateFri, 16 Jun 2017 03:37:56 +0000
reviewerscpearce
bugs1371882
milestone56.0a1
Bug 1371882 - Avoid MemoryBlockCache when combined sizes > 'media.memory_caches_combined_limit_...' - r?cpearce Don't go over the lowest of 'media.memory_caches_combined_limit_kb' (kilobytes) or 'media.memory_caches_combined_limit_pc_sysmem' (percents of system memory). Added more logging around creation/destruction of MediaCaches. MozReview-Commit-ID: Cdz4ycyn1RR
dom/media/MediaCache.cpp
dom/media/MediaPrefs.h
modules/libpref/init/all.js
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -18,16 +18,17 @@
 #include "MemoryBlockCache.h"
 #include "nsIObserverService.h"
 #include "nsISeekableStream.h"
 #include "nsIPrincipal.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
+#include "prsystem.h"
 #include <algorithm>
 
 namespace mozilla {
 
 #undef LOG
 #undef LOGI
 LazyLogModule gMediaCacheLog("MediaCache");
 #define LOG(...) MOZ_LOG(gMediaCacheLog, LogLevel::Debug, (__VA_ARGS__))
@@ -399,16 +400,19 @@ protected:
   // Shutdown this MediaCache, and reset gMediaCache if we are the global one.
   // If there is no queued update, destroy the MediaCache immediately.
   // Otherwise when the update is processed, it will destroy the MediaCache.
   void ShutdownAndDestroyThis();
 
   // There is at most one file-backed media cache.
   static MediaCache* gMediaCache;
 
+  // Combined size of all memory-backed MediaCaches. Main-thread only.
+  static int64_t gMediaMemoryCachesCombinedSize;
+
   // Expected content length if known initially from the HTTP Content-Length
   // header (this is a memory-backed MediaCache), otherwise -1 (file-backed
   // MediaCache).
   const int64_t mContentLength;
 
   // This member is main-thread only. It's used to allocate unique
   // resource IDs to streams.
   int64_t                       mNextResourceID;
@@ -439,16 +443,18 @@ protected:
   bool            mInUpdate;
 #endif
   // A list of resource IDs to notify about the change in suspended status.
   nsTArray<int64_t> mSuspendedStatusToNotify;
 };
 
 // Initialized to nullptr by non-local static initialization.
 /* static */ MediaCache* MediaCache::gMediaCache;
+// Initialized to 0 by non-local static initialization.
+/* static */ int64_t MediaCache::gMediaMemoryCachesCombinedSize;
 
 NS_IMETHODIMP
 MediaCacheFlusher::Observe(nsISupports *aSubject, char const *aTopic, char16_t const *aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (strcmp(aTopic, "last-pb-context-exited") == 0) {
     for (MediaCache* mc : mMediaCaches) {
@@ -748,46 +754,75 @@ MediaCache::ShutdownAndDestroyThis()
   }
 
   if (mUpdateQueued) {
     // An update is queued, let it destroy this MediaCache object.
     mShutdownInsteadOfUpdating = true;
     return;
   }
 
+  if (mContentLength > 0) {
+    // This is an memory-backed MediaCache, update the combined memory usage.
+    gMediaMemoryCachesCombinedSize -= mContentLength;
+    LOG("ShutdownAndDestroyThis(Memory-backed MediaCache %p) -> combined size now %" PRIi64,
+        this,
+        gMediaMemoryCachesCombinedSize);
+  } else {
+    LOG("ShutdownAndDestroyThis(Global file-backed MediaCache)");
+  }
+
   delete this;
 }
 
 /* static */ MediaCache*
 MediaCache::GetMediaCache(int64_t aContentLength)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  static const size_t sysmem = std::max<size_t>(PR_GetPhysicalMemorySize(), 32*1024*1024);
   if (aContentLength > 0 &&
       aContentLength <=
-        int64_t(MediaPrefs::MediaMemoryCacheMaxSize()) * 1024) {
-    // Small-enough resource, use a new memory-backed MediaCache.
+        int64_t(MediaPrefs::MediaMemoryCacheMaxSize()) * 1024 &&
+      size_t(gMediaMemoryCachesCombinedSize + aContentLength) <=
+        std::min(
+        size_t(MediaPrefs::MediaMemoryCachesCombinedLimitKb()) * 1024,
+        sysmem * MediaPrefs::MediaMemoryCachesCombinedLimitPcSysmem() / 100)) {
+    // Small-enough resource (and we are under the maximum memory usage), use
+    // a new memory-backed MediaCache.
     MediaCache* mc = new MediaCache(aContentLength);
     nsresult rv = mc->Init();
     if (NS_SUCCEEDED(rv)) {
+      gMediaMemoryCachesCombinedSize += aContentLength;
+      LOG("GetMediaCache(%" PRIi64
+          ") -> Memory MediaCache %p, combined size %" PRIi64,
+          aContentLength,
+          mc,
+          gMediaMemoryCachesCombinedSize);
       return mc;
     }
     // Memory-backed MediaCache initialization failed, clean up and try for a
     // file-backed MediaCache below.
     delete mc;
   }
 
   if (gMediaCache) {
+    LOG("GetMediaCache(%" PRIi64 ") -> Existing file-backed MediaCache",
+        aContentLength);
     return gMediaCache;
   }
 
   gMediaCache = new MediaCache(-1);
   nsresult rv = gMediaCache->Init();
   if (NS_FAILED(rv)) {
     delete gMediaCache;
     gMediaCache = nullptr;
+    LOG("GetMediaCache(%" PRIi64 ") -> Failed to create file-backed MediaCache",
+        aContentLength);
+  } else {
+    LOG("GetMediaCache(%" PRIi64 ") -> Created file-backed MediaCache",
+        aContentLength);
   }
 
   return gMediaCache;
 }
 
 nsresult
 MediaCache::ReadCacheFile(
   int64_t aOffset, void* aData, int32_t aLength, int32_t* aBytes)
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -83,16 +83,19 @@ private:
       PrefAddVarCache(&mValue, aPreference, mValue);
     }
   };
 
   // This is where DECL_MEDIA_PREF for each of the preferences should go.
 
   // Cache sizes.
   DECL_MEDIA_PREF("media.memory_cache_max_size",              MediaMemoryCacheMaxSize, uint32_t, 8192);
+  DECL_MEDIA_PREF("media.memory_caches_combined_limit_kb",    MediaMemoryCachesCombinedLimitKb, uint32_t, 524288);
+  DECL_MEDIA_PREF("media.memory_caches_combined_limit_pc_sysmem",
+                                                              MediaMemoryCachesCombinedLimitPcSysmem, uint32_t, 5);
   DECL_MEDIA_PREF("media.cache.resource-index",               MediaResourceIndexCache, uint32_t, 8192);
 
   // AudioSink
   DECL_MEDIA_PREF("accessibility.monoaudio.enable",           MonoAudio, bool, false);
   DECL_MEDIA_PREF("media.resampling.enabled",                 AudioSinkResampling, bool, false);
   DECL_MEDIA_PREF("media.resampling.rate",                    AudioSinkResampleRate, uint32_t, 48000);
 #if defined(XP_WIN) || defined(XP_DARWIN) || defined(MOZ_PULSEAUDIO)
   // libcubeb backend implement .get_preferred_channel_layout
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -325,16 +325,20 @@ pref("media.cache_resume_threshold", 30)
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.
 pref("media.cache_readahead_limit", 60);
 // If a resource is known to be smaller than this size (in kilobytes), a
 // memory-backed MediaCache may be used; otherwise the (single shared
 // global) file-backed MediaCache is used.
 pref("media.memory_cache_max_size", 8192);
+// Don't create more memory-backed MediaCaches if their combined size would go
+// above the lowest limit (in kilobytes or in percent of physical memory size).
+pref("media.memory_caches_combined_limit_kb", 524288);
+pref("media.memory_caches_combined_limit_pc_sysmem", 5);
 
 // Cache size hint (in bytes) for each MediaResourceIndex.
 // 0 -> no cache. Will use next power of 2, clamped to 32B-128KB.
 pref("media.cache.resource-index", 8192);
 
 // We'll throttle the download if the download rate is throttle-factor times
 // the estimated playback rate, AND we satisfy the cache readahead_limit
 // above. The estimated playback rate is time_duration/length_in_bytes.