Bug 1120271 - Add compacting support for SourceBuffer. r=tn

This commit is contained in:
Seth Fowler 2015-01-15 20:28:38 -08:00
parent 8faea231e2
commit b58c9ccafd
2 changed files with 135 additions and 14 deletions

View File

@ -22,6 +22,13 @@ namespace image {
// SourceBufferIterator implementation.
//////////////////////////////////////////////////////////////////////////////
SourceBufferIterator::~SourceBufferIterator()
{
if (mOwner) {
mOwner->OnIteratorRelease();
}
}
SourceBufferIterator::State
SourceBufferIterator::AdvanceOrScheduleResume(IResumable* aConsumer)
{
@ -43,8 +50,15 @@ SourceBufferIterator::RemainingBytesIsNoMoreThan(size_t aBytes) const
SourceBuffer::SourceBuffer()
: mMutex("image::SourceBuffer")
, mConsumerCount(0)
{ }
SourceBuffer::~SourceBuffer()
{
MOZ_ASSERT(mConsumerCount == 0,
"SourceBuffer destroyed with active consumers");
}
nsresult
SourceBuffer::AppendChunk(Maybe<Chunk>&& aChunk)
{
@ -72,16 +86,87 @@ SourceBuffer::AppendChunk(Maybe<Chunk>&& aChunk)
}
Maybe<SourceBuffer::Chunk>
SourceBuffer::CreateChunk(size_t aCapacity)
SourceBuffer::CreateChunk(size_t aCapacity, bool aRoundUp /* = true */)
{
if (MOZ_UNLIKELY(aCapacity == 0)) {
MOZ_ASSERT_UNREACHABLE("Appending a chunk of zero size?");
return Nothing();
}
// Round up if requested.
size_t finalCapacity = aRoundUp ? RoundedUpCapacity(aCapacity)
: aCapacity;
// Use the size of the SurfaceCache as an additional heuristic to avoid
// allocating huge buffers. Generally images do not get smaller when decoded,
// so if we could store the source data in the SurfaceCache, we assume that
// there's no way we'll be able to store the decoded version.
if (MOZ_UNLIKELY(!SurfaceCache::CanHold(finalCapacity))) {
return Nothing();
}
return Some(Chunk(finalCapacity));
}
nsresult
SourceBuffer::Compact()
{
mMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(mConsumerCount == 0, "Should have no consumers here");
MOZ_ASSERT(mWaitingConsumers.Length() == 0, "Shouldn't have waiters");
MOZ_ASSERT(mStatus, "Should be complete here");
// Compact our waiting consumers list, since we're complete and no future
// consumer will ever have to wait.
mWaitingConsumers.Compact();
// If we have no more than one chunk, then we can't compact further.
if (mChunks.Length() < 2) {
return NS_OK;
}
// We can compact our buffer. Determine the total length.
size_t length = 0;
for (uint32_t i = 0 ; i < mChunks.Length() ; ++i) {
length += mChunks[i].Length();
}
Maybe<Chunk> newChunk = CreateChunk(length, /* aRoundUp = */ false);
if (MOZ_UNLIKELY(!newChunk || newChunk->AllocationFailed())) {
NS_WARNING("Failed to allocate chunk for SourceBuffer compacting - OOM?");
return NS_OK;
}
// Copy our old chunks into the new chunk.
for (uint32_t i = 0 ; i < mChunks.Length() ; ++i) {
size_t offset = newChunk->Length();
MOZ_ASSERT(offset < newChunk->Capacity());
MOZ_ASSERT(offset + mChunks[i].Length() <= newChunk->Capacity());
memcpy(newChunk->Data() + offset, mChunks[i].Data(), mChunks[i].Length());
newChunk->AddLength(mChunks[i].Length());
}
MOZ_ASSERT(newChunk->Length() == newChunk->Capacity(),
"Compacted chunk has slack space");
// Replace the old chunks with the new, compact chunk.
mChunks.Clear();
if (MOZ_UNLIKELY(NS_FAILED(AppendChunk(Move(newChunk))))) {
return HandleError(NS_ERROR_OUT_OF_MEMORY);
}
mChunks.Compact();
return NS_OK;
}
/* static */ size_t
SourceBuffer::RoundedUpCapacity(size_t aCapacity)
{
// Protect against overflow.
if (MOZ_UNLIKELY(SIZE_MAX - aCapacity < MIN_CHUNK_CAPACITY)) {
return Nothing();
return aCapacity;
}
// Round up to the next multiple of MIN_CHUNK_CAPACITY (which should be the
@ -91,15 +176,7 @@ SourceBuffer::CreateChunk(size_t aCapacity)
MOZ_ASSERT(roundedCapacity >= aCapacity, "Bad math?");
MOZ_ASSERT(roundedCapacity - aCapacity < MIN_CHUNK_CAPACITY, "Bad math?");
// Use the size of the SurfaceCache as an additional heuristic to avoid
// allocating huge buffers. Generally images do not get smaller when decoded,
// so if we could store the source data in the SurfaceCache, we assume that
// there's no way we'll be able to store the decoded version.
if (MOZ_UNLIKELY(!SurfaceCache::CanHold(roundedCapacity))) {
return Nothing();
}
return Some(Chunk(roundedCapacity));
return roundedCapacity;
}
size_t
@ -291,6 +368,14 @@ SourceBuffer::Complete(nsresult aStatus)
// Resume any waiting consumers now that we're complete.
ResumeWaitingConsumers();
// If we still have active consumers, just return.
if (mConsumerCount > 0) {
return;
}
// Attempt to compact our buffer down to a single chunk.
Compact();
}
bool
@ -323,6 +408,34 @@ SourceBuffer::SizeOfIncludingThisWithComputedFallback(MallocSizeOf
return n;
}
SourceBufferIterator
SourceBuffer::Iterator()
{
{
MutexAutoLock lock(mMutex);
mConsumerCount++;
}
return SourceBufferIterator(this);
}
void
SourceBuffer::OnIteratorRelease()
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mConsumerCount > 0, "Consumer count doesn't add up");
mConsumerCount--;
// If we still have active consumers, or we're not complete yet, then return.
if (mConsumerCount > 0 || !mStatus) {
return;
}
// Attempt to compact our buffer down to a single chunk.
Compact();
}
bool
SourceBuffer::RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
size_t aBytes) const

View File

@ -91,6 +91,8 @@ public:
, mData(aOther.mData)
{ }
~SourceBufferIterator();
SourceBufferIterator& operator=(SourceBufferIterator&& aOther)
{
mOwner = Move(aOther.mOwner);
@ -250,13 +252,13 @@ public:
//////////////////////////////////////////////////////////////////////////////
/// Returns an iterator to this SourceBuffer.
SourceBufferIterator Iterator() { return SourceBufferIterator(this); }
SourceBufferIterator Iterator();
private:
friend class SourceBufferIterator;
~SourceBuffer() { }
~SourceBuffer();
//////////////////////////////////////////////////////////////////////////////
// Chunk type and chunk-related methods.
@ -321,7 +323,9 @@ private:
};
nsresult AppendChunk(Maybe<Chunk>&& aChunk);
Maybe<Chunk> CreateChunk(size_t aCapacity);
Maybe<Chunk> CreateChunk(size_t aCapacity, bool aRoundUp = true);
nsresult Compact();
static size_t RoundedUpCapacity(size_t aCapacity);
size_t FibonacciCapacityWithMinimum(size_t aMinCapacity);
@ -339,6 +343,7 @@ private:
bool RemainingBytesIsNoMoreThan(const SourceBufferIterator& aIterator,
size_t aBytes) const;
void OnIteratorRelease();
//////////////////////////////////////////////////////////////////////////////
// Helper methods.
@ -366,6 +371,9 @@ private:
/// If present, marks this SourceBuffer complete with the given final status.
Maybe<nsresult> mStatus;
/// Count of active consumers.
uint32_t mConsumerCount;
};
} // namespace image