Bug 1685822 [wpt PR 27117] - [Import Maps] Add tests for rejecting multiple import...
[gecko.git] / dom / media / MemoryBlockCache.cpp
blob2848a3f3812cb29ca1fc2531bc70a9fdb50c27f7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MemoryBlockCache.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/Logging.h"
12 #include "mozilla/Services.h"
13 #include "mozilla/StaticPrefs_media.h"
14 #include "nsWeakReference.h"
15 #include "prsystem.h"
17 namespace mozilla {
19 #undef LOG
20 LazyLogModule gMemoryBlockCacheLog("MemoryBlockCache");
21 #define LOG(x, ...) \
22 MOZ_LOG(gMemoryBlockCacheLog, LogLevel::Debug, ("%p " x, this, ##__VA_ARGS__))
24 // Combined sizes of all MemoryBlockCache buffers.
25 // Initialized to 0 by non-local static initialization.
26 // Increases when a buffer grows (during initialization or unexpected OOB
27 // writes), decreases when a MemoryBlockCache (with its buffer) is destroyed.
28 static Atomic<size_t> gCombinedSizes;
30 static int32_t CalculateMaxBlocks(int64_t aContentLength) {
31 int64_t maxSize = int64_t(StaticPrefs::media_memory_cache_max_size()) * 1024;
32 MOZ_ASSERT(aContentLength <= maxSize);
33 MOZ_ASSERT(maxSize % MediaBlockCacheBase::BLOCK_SIZE == 0);
34 // Note: It doesn't matter if calculations overflow, Init() would later fail.
35 // We want at least enough blocks to contain the original content length.
36 const int32_t requiredBlocks = maxSize / MediaBlockCacheBase::BLOCK_SIZE;
37 // Allow at least 1s of ultra HD (25Mbps).
38 const int32_t workableBlocks =
39 25 * 1024 * 1024 / 8 / MediaBlockCacheBase::BLOCK_SIZE;
40 return std::max(requiredBlocks, workableBlocks);
43 MemoryBlockCache::MemoryBlockCache(int64_t aContentLength)
44 // Buffer whole blocks.
45 : mInitialContentLength((aContentLength >= 0) ? size_t(aContentLength) : 0),
46 mMaxBlocks(CalculateMaxBlocks(aContentLength)),
47 mMutex("MemoryBlockCache"),
48 mHasGrown(false) {
49 if (aContentLength <= 0) {
50 LOG("MemoryBlockCache() MEMORYBLOCKCACHE_ERRORS='InitUnderuse'");
54 MemoryBlockCache::~MemoryBlockCache() {
55 MOZ_ASSERT(gCombinedSizes >= mBuffer.Length());
56 size_t sizes = static_cast<size_t>(gCombinedSizes -= mBuffer.Length());
57 LOG("~MemoryBlockCache() - destroying buffer of size %zu; combined sizes now "
58 "%zu",
59 mBuffer.Length(), sizes);
62 bool MemoryBlockCache::EnsureBufferCanContain(size_t aContentLength) {
63 mMutex.AssertCurrentThreadOwns();
64 if (aContentLength == 0) {
65 return true;
67 const size_t initialLength = mBuffer.Length();
68 const size_t desiredLength =
69 ((aContentLength - 1) / BLOCK_SIZE + 1) * BLOCK_SIZE;
70 if (initialLength >= desiredLength) {
71 // Already large enough.
72 return true;
74 // Need larger buffer. If we are allowed more memory, attempt to re-allocate.
75 const size_t extra = desiredLength - initialLength;
76 // Only check the very first allocation against the combined MemoryBlockCache
77 // limit. Further growths will always be allowed, assuming MediaCache won't
78 // go over GetMaxBlocks() by too much.
79 if (initialLength == 0) {
80 // Note: There is a small race between testing `atomic + extra > limit` and
81 // committing to it with `atomic += extra` below; but this is acceptable, as
82 // in the worst case it may allow a small number of buffers to go past the
83 // limit.
84 // The alternative would have been to reserve the space first with
85 // `atomic += extra` and then undo it with `atomic -= extra` in case of
86 // failure; but this would have meant potentially preventing other (small
87 // but successful) allocations.
88 static const size_t sysmem =
89 std::max<size_t>(PR_GetPhysicalMemorySize(), 32 * 1024 * 1024);
90 const size_t limit = std::min(
91 size_t(StaticPrefs::media_memory_caches_combined_limit_kb()) * 1024,
92 sysmem * StaticPrefs::media_memory_caches_combined_limit_pc_sysmem() /
93 100);
94 const size_t currentSizes = static_cast<size_t>(gCombinedSizes);
95 if (currentSizes + extra > limit) {
96 LOG("EnsureBufferCanContain(%zu) - buffer size %zu, wanted + %zu = %zu;"
97 " combined sizes %zu + %zu > limit %zu",
98 aContentLength, initialLength, extra, desiredLength, currentSizes,
99 extra, limit);
100 return false;
103 if (!mBuffer.SetLength(desiredLength, mozilla::fallible)) {
104 LOG("EnsureBufferCanContain(%zu) - buffer size %zu, wanted + %zu = %zu, "
105 "allocation failed",
106 aContentLength, initialLength, extra, desiredLength);
107 return false;
109 MOZ_ASSERT(mBuffer.Length() == desiredLength);
110 const size_t capacity = mBuffer.Capacity();
111 const size_t extraCapacity = capacity - desiredLength;
112 if (extraCapacity != 0) {
113 // Our buffer was given a larger capacity than the requested length, we may
114 // as well claim that extra capacity, both for our accounting, and to
115 // possibly bypass some future growths that would fit in this new capacity.
116 mBuffer.SetLength(capacity);
118 const size_t newSizes = gCombinedSizes += (extra + extraCapacity);
119 LOG("EnsureBufferCanContain(%zu) - buffer size %zu + requested %zu + bonus "
120 "%zu = %zu; combined sizes %zu",
121 aContentLength, initialLength, extra, extraCapacity, capacity, newSizes);
122 mHasGrown = true;
123 return true;
126 nsresult MemoryBlockCache::Init() {
127 LOG("Init()");
128 MutexAutoLock lock(mMutex);
129 MOZ_ASSERT(mBuffer.IsEmpty());
130 // Attempt to pre-allocate buffer for expected content length.
131 if (!EnsureBufferCanContain(mInitialContentLength)) {
132 LOG("Init() MEMORYBLOCKCACHE_ERRORS='InitAllocation'");
133 return NS_ERROR_FAILURE;
135 return NS_OK;
138 void MemoryBlockCache::Flush() {
139 LOG("Flush()");
140 MutexAutoLock lock(mMutex);
141 MOZ_ASSERT(mBuffer.Length() >= mInitialContentLength);
142 memset(mBuffer.Elements(), 0, mBuffer.Length());
143 mHasGrown = false;
146 nsresult MemoryBlockCache::WriteBlock(uint32_t aBlockIndex,
147 Span<const uint8_t> aData1,
148 Span<const uint8_t> aData2) {
149 MutexAutoLock lock(mMutex);
151 size_t offset = BlockIndexToOffset(aBlockIndex);
152 if (offset + aData1.Length() + aData2.Length() > mBuffer.Length() &&
153 !mHasGrown) {
154 LOG("WriteBlock() MEMORYBLOCKCACHE_ERRORS='WriteBlockOverflow'");
156 if (!EnsureBufferCanContain(offset + aData1.Length() + aData2.Length())) {
157 LOG("WriteBlock() MEMORYBLOCKCACHE_ERRORS='WriteBlockCannotGrow'");
158 return NS_ERROR_FAILURE;
161 memcpy(mBuffer.Elements() + offset, aData1.Elements(), aData1.Length());
162 if (aData2.Length() > 0) {
163 memcpy(mBuffer.Elements() + offset + aData1.Length(), aData2.Elements(),
164 aData2.Length());
167 return NS_OK;
170 nsresult MemoryBlockCache::Read(int64_t aOffset, uint8_t* aData,
171 int32_t aLength, int32_t* aBytes) {
172 MutexAutoLock lock(mMutex);
174 MOZ_ASSERT(aOffset >= 0);
175 if (aOffset + aLength > int64_t(mBuffer.Length())) {
176 LOG("Read() MEMORYBLOCKCACHE_ERRORS='ReadOverrun'");
177 return NS_ERROR_FAILURE;
180 memcpy(aData, mBuffer.Elements() + aOffset, aLength);
181 *aBytes = aLength;
183 return NS_OK;
186 nsresult MemoryBlockCache::MoveBlock(int32_t aSourceBlockIndex,
187 int32_t aDestBlockIndex) {
188 MutexAutoLock lock(mMutex);
190 size_t sourceOffset = BlockIndexToOffset(aSourceBlockIndex);
191 size_t destOffset = BlockIndexToOffset(aDestBlockIndex);
192 if (sourceOffset + BLOCK_SIZE > mBuffer.Length()) {
193 LOG("MoveBlock() MEMORYBLOCKCACHE_ERRORS='MoveBlockSourceOverrun'");
194 return NS_ERROR_FAILURE;
196 if (destOffset + BLOCK_SIZE > mBuffer.Length() && !mHasGrown) {
197 LOG("MoveBlock() MEMORYBLOCKCACHE_ERRORS='MoveBlockDestOverflow'");
199 if (!EnsureBufferCanContain(destOffset + BLOCK_SIZE)) {
200 LOG("MoveBlock() MEMORYBLOCKCACHE_ERRORS='MoveBlockCannotGrow'");
201 return NS_ERROR_FAILURE;
204 memcpy(mBuffer.Elements() + destOffset, mBuffer.Elements() + sourceOffset,
205 BLOCK_SIZE);
207 return NS_OK;
210 } // End namespace mozilla.
212 // avoid redefined macro in unified build
213 #undef LOG