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
--- 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.