Bug 1379091 - Let block cache tell MediaCache its block use limit - r=cpearce

MozReview-Commit-ID: 5ZCD3NoeYEP

--HG--
extra : rebase_source : 0fe76cfa6b15053dc5cf2b392c5d649e6888e6c6
This commit is contained in:
Gerald Squelart 2017-07-10 10:23:02 +12:00
parent 98e6346e07
commit fd79ed9c4e
6 changed files with 64 additions and 28 deletions

View File

@ -5,6 +5,8 @@
* 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"
@ -121,6 +123,33 @@ FileBlockCache::Init()
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)

View File

@ -65,6 +65,10 @@ public:
// 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,

View File

@ -52,6 +52,10 @@ public:
// 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,

View File

@ -8,6 +8,7 @@
#include "FileBlockCache.h"
#include "MediaBlockCacheBase.h"
#include "MediaPrefs.h"
#include "MediaResource.h"
#include "MemoryBlockCache.h"
#include "mozilla/Attributes.h"
@ -737,31 +738,6 @@ MediaCache::ReadCacheFile(
}
}
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)
@ -818,8 +794,10 @@ MediaCache::FindBlockForIncomingData(TimeStamp aNow,
// 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;
@ -1163,7 +1141,7 @@ MediaCache::Update()
mInUpdate = true;
#endif
int32_t maxBlocks = GetMaxBlocks();
int32_t maxBlocks = mBlockCache->GetMaxBlocks();
TimeStamp now = TimeStamp::Now();
int32_t freeBlockCount = mFreeBlocks.GetCount();

View File

@ -137,9 +137,23 @@ enum MemoryBlockCacheTelemetryErrors
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)
{

View File

@ -42,6 +42,10 @@ public:
// 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,
@ -71,6 +75,9 @@ private:
// 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;