Bug 1379091 - Let block cache tell MediaCache its block use limit - r=cpearce
authorGerald Squelart <gsquelart@mozilla.com>
Mon, 10 Jul 2017 10:23:02 +1200
changeset 368155 82220cb42a60e574a0945bc7a959cc8c5cadc10d
parent 368154 dec7cb09336ee273f362ce0550a36ef70d5202d3
child 368156 a7e5cdf5c07f2e0b61fb7f5d62a7f080a7df19fb
push id32158
push usercbook@mozilla.com
push dateTue, 11 Jul 2017 10:48:59 +0000
treeherdermozilla-central@5e2692f8a367 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1379091
milestone56.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 1379091 - Let block cache tell MediaCache its block use limit - r=cpearce MozReview-Commit-ID: 5ZCD3NoeYEP
dom/media/FileBlockCache.cpp
dom/media/FileBlockCache.h
dom/media/MediaBlockCacheBase.h
dom/media/MediaCache.cpp
dom/media/MemoryBlockCache.cpp
dom/media/MemoryBlockCache.h
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileBlockCache.h"
+#include "MediaCache.h"
+#include "MediaPrefs.h"
 #include "mozilla/SharedThreadPool.h"
 #include "VideoUtils.h"
 #include "prio.h"
 #include <algorithm>
 #include "nsAnonymousTemporaryFile.h"
 #include "mozilla/dom/ContentChild.h"
 #include "nsXULAppAPI.h"
 
@@ -116,16 +118,43 @@ FileBlockCache::Init()
 
   if (NS_FAILED(rv)) {
     Close();
   }
 
   return rv;
 }
 
+int32_t
+FileBlockCache::GetMaxBlocks() const
+{
+  // We look up the cache size every time. This means dynamic changes
+  // to the pref are applied.
+  const uint32_t cacheSizeKb =
+    std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
+  // Ensure we can divide BLOCK_SIZE by 1024.
+  static_assert(MediaCacheStream::BLOCK_SIZE % 1024 == 0,
+                "BLOCK_SIZE should be a multiple of 1024");
+  // Ensure BLOCK_SIZE/1024 is at least 2.
+  static_assert(MediaCacheStream::BLOCK_SIZE / 1024 >= 2,
+                "BLOCK_SIZE / 1024 should be at least 2");
+  // Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
+  static_assert(MediaCacheStream::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
+                "BLOCK_SIZE / 1024 should be at most UINT32_MAX");
+  // Since BLOCK_SIZE is a strict multiple of 1024,
+  // cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
+  // but the latter formula avoids a potential overflow from `* 1024`.
+  // And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
+  // INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
+  constexpr uint32_t blockSizeKb =
+    uint32_t(MediaCacheStream::BLOCK_SIZE / 1024);
+  const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
+  return std::max(maxBlocks, int32_t(1));
+}
+
 FileBlockCache::FileBlockCache()
   : mFileMutex("MediaCache.Writer.IO.Mutex")
   , mFD(nullptr)
   , mFDCurrentPos(0)
   , mDataMutex("MediaCache.Writer.Data.Mutex")
   , mIsWriteScheduled(false)
   , mIsReading(false)
 {
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -60,16 +60,20 @@ public:
 protected:
   virtual ~FileBlockCache();
 
 public:
   // Launch thread and open temporary file.
   // If re-initializing, just discard pending writes if any.
   nsresult Init() override;
 
+  // Maximum number of blocks allowed in this block cache.
+  // Calculated from "media.cache_size" pref.
+  int32_t GetMaxBlocks() const override;
+
   // Can be called on any thread. This defers to a non-main thread.
   nsresult WriteBlock(uint32_t aBlockIndex,
                       Span<const uint8_t> aData1,
                       Span<const uint8_t> aData2) override;
 
   // Synchronously reads data from file. May read from file or memory
   // depending on whether written blocks have been flushed to file yet.
   // Not recommended to be called from the main thread, as can cause jank.
--- a/dom/media/MediaBlockCacheBase.h
+++ b/dom/media/MediaBlockCacheBase.h
@@ -47,16 +47,20 @@ public:
 protected:
   virtual ~MediaBlockCacheBase() {}
 
 public:
   // Initialize this cache.
   // If called again, re-initialize cache with minimal chance of failure.
   virtual nsresult Init() = 0;
 
+  // Maximum number of blocks expected in this block cache. (But allow overflow
+  // to accomodate incoming traffic before MediaCache can handle it.)
+  virtual int32_t GetMaxBlocks() const = 0;
+
   // Can be called on any thread. This defers to a non-main thread.
   virtual nsresult WriteBlock(uint32_t aBlockIndex,
                               Span<const uint8_t> aData1,
                               Span<const uint8_t> aData2) = 0;
 
   // Synchronously reads data from file. May read from file or memory
   // depending on whether written blocks have been flushed to file yet.
   // Not recommended to be called from the main thread, as can cause jank.
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaCache.h"
 
 #include "FileBlockCache.h"
 #include "MediaBlockCacheBase.h"
+#include "MediaPrefs.h"
 #include "MediaResource.h"
 #include "MemoryBlockCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -732,41 +733,16 @@ MediaCache::ReadCacheFile(
     // Since the monitor might be acquired on the main thread, we need to drop
     // the monitor while doing IO in order not to block the main thread.
     ReentrantMonitorAutoExit unlock(mReentrantMonitor);
     return blockCache->Read(
       aOffset, reinterpret_cast<uint8_t*>(aData), aLength, aBytes);
   }
 }
 
-static int32_t GetMaxBlocks()
-{
-  // We look up the cache size every time. This means dynamic changes
-  // to the pref are applied.
-  const uint32_t cacheSizeKb =
-    std::min(MediaPrefs::MediaCacheSizeKb(), uint32_t(INT32_MAX) * 2);
-  // Ensure we can divide BLOCK_SIZE by 1024.
-  static_assert(MediaCache::BLOCK_SIZE % 1024 == 0,
-                "BLOCK_SIZE should be a multiple of 1024");
-  // Ensure BLOCK_SIZE/1024 is at least 2.
-  static_assert(MediaCache::BLOCK_SIZE / 1024 >= 2,
-                "BLOCK_SIZE / 1024 should be at least 2");
-  // Ensure we can convert BLOCK_SIZE/1024 to a uint32_t without truncation.
-  static_assert(MediaCache::BLOCK_SIZE / 1024 <= int64_t(UINT32_MAX),
-                "BLOCK_SIZE / 1024 should be at most UINT32_MAX");
-  // Since BLOCK_SIZE is a strict multiple of 1024,
-  // cacheSizeKb * 1024 / BLOCK_SIZE == cacheSizeKb / (BLOCK_SIZE / 1024),
-  // but the latter formula avoids a potential overflow from `* 1024`.
-  // And because BLOCK_SIZE/1024 is at least 2, the maximum cache size
-  // INT32_MAX*2 will give a maxBlocks that can fit in an int32_t.
-  constexpr uint32_t blockSizeKb = uint32_t(MediaCache::BLOCK_SIZE / 1024);
-  const int32_t maxBlocks = int32_t(cacheSizeKb / blockSizeKb);
-  return std::max(maxBlocks, int32_t(1));
-}
-
 // Allowed range is whatever can be accessed with an int32_t block index.
 static bool
 IsOffsetAllowed(int64_t aOffset)
 {
   return aOffset < (int64_t(INT32_MAX) + 1) * MediaCache::BLOCK_SIZE &&
          aOffset >= 0;
 }
 
@@ -813,18 +789,20 @@ MediaCache::FindBlockForIncomingData(Tim
                       INT32_MAX);
 
   if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
     // The block returned is already allocated.
     // Don't reuse it if a) there's room to expand the cache or
     // b) the data we're going to store in the free block is not higher
     // priority than the data already stored in the free block.
     // The latter can lead us to go over the cache limit a bit.
-    if ((mIndex.Length() < uint32_t(GetMaxBlocks()) || blockIndex < 0 ||
-         PredictNextUseForIncomingData(aStream) >= PredictNextUse(aNow, blockIndex))) {
+    if ((mIndex.Length() < uint32_t(mBlockCache->GetMaxBlocks()) ||
+         blockIndex < 0 ||
+         PredictNextUseForIncomingData(aStream) >=
+           PredictNextUse(aNow, blockIndex))) {
       blockIndex = mIndex.Length();
       if (!mIndex.AppendElement())
         return -1;
       mIndexWatermark = std::max(mIndexWatermark, blockIndex + 1);
       mFreeBlocks.AddFirstBlock(blockIndex);
       return blockIndex;
     }
   }
@@ -1158,17 +1136,17 @@ MediaCache::Update()
 
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     mUpdateQueued = false;
 #ifdef DEBUG
     mInUpdate = true;
 #endif
 
-    int32_t maxBlocks = GetMaxBlocks();
+    int32_t maxBlocks = mBlockCache->GetMaxBlocks();
     TimeStamp now = TimeStamp::Now();
 
     int32_t freeBlockCount = mFreeBlocks.GetCount();
     TimeDuration latestPredictedUseForOverflow = 0;
     if (mIndex.Length() > uint32_t(maxBlocks)) {
       // Try to trim back the cache to its desired maximum size. The cache may
       // have overflowed simply due to data being received when we have
       // no blocks in the main part of the cache that are free or lower
--- a/dom/media/MemoryBlockCache.cpp
+++ b/dom/media/MemoryBlockCache.cpp
@@ -132,19 +132,33 @@ enum MemoryBlockCacheTelemetryErrors
   ReadOverrun = 2,
   WriteBlockOverflow = 3,
   WriteBlockCannotGrow = 4,
   MoveBlockSourceOverrun = 5,
   MoveBlockDestOverflow = 6,
   MoveBlockCannotGrow = 7,
 };
 
+static int32_t
+CalculateMaxBlocks(int64_t aContentLength)
+{
+  // Note: It doesn't matter if calculations overflow, Init() would later fail.
+  // We want at least enough blocks to contain the original content length.
+  const int32_t requiredBlocks =
+    int32_t((aContentLength - 1) / MediaBlockCacheBase::BLOCK_SIZE + 1);
+  // Allow at least 1s of ultra HD (25Mbps).
+  const int32_t workableBlocks =
+    25 * 1024 * 1024 / 8 / MediaBlockCacheBase::BLOCK_SIZE;
+  return std::max(requiredBlocks, workableBlocks);
+}
+
 MemoryBlockCache::MemoryBlockCache(int64_t aContentLength)
   // Buffer whole blocks.
   : mInitialContentLength((aContentLength >= 0) ? size_t(aContentLength) : 0)
+  , mMaxBlocks(CalculateMaxBlocks(aContentLength))
   , mMutex("MemoryBlockCache")
   , mHasGrown(false)
 {
   if (aContentLength <= 0) {
     LOG("MemoryBlockCache() MEMORYBLOCKCACHE_ERRORS='InitUnderuse'");
     Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
                           InitUnderuse);
   }
--- a/dom/media/MemoryBlockCache.h
+++ b/dom/media/MemoryBlockCache.h
@@ -37,16 +37,20 @@ public:
 protected:
   virtual ~MemoryBlockCache();
 
 public:
   // Allocate initial buffer.
   // If re-initializing, clear buffer.
   virtual nsresult Init() override;
 
+  // Maximum number of blocks allowed in this block cache.
+  // Based on initial content length, and minimum usable block cache.
+  int32_t GetMaxBlocks() const override { return mMaxBlocks; }
+
   // Can be called on any thread.
   virtual nsresult WriteBlock(uint32_t aBlockIndex,
                               Span<const uint8_t> aData1,
                               Span<const uint8_t> aData2) override;
 
   // Synchronously reads data from buffer.
   virtual nsresult Read(int64_t aOffset,
                         uint8_t* aData,
@@ -66,16 +70,19 @@ private:
   // Ensure the buffer has at least a multiple of BLOCK_SIZE that can contain
   // aContentLength bytes. Buffer size can only grow.
   // Returns false if allocation failed.
   bool EnsureBufferCanContain(size_t aContentLength);
 
   // Initial content length.
   const size_t mInitialContentLength;
 
+  // Maximum number of blocks that this MemoryBlockCache expects.
+  const int32_t mMaxBlocks;
+
   // Mutex which controls access to all members below.
   Mutex mMutex;
 
   nsTArray<uint8_t> mBuffer;
   bool mHasGrown;
 };
 
 } // End namespace mozilla.