/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// We always keep at least two buffers. This means that when we // have to add a new buffer, there is at least a full buffer that requires // translating while the handle is sent over.
AutoTArray<Handle, 2> bufferHandles; auto buffer = CreateAndMapShmem(mDefaultBufferSize); if (NS_WARN_IF(buffer.isNothing())) { returnfalse;
}
mCurrentBuffer = CanvasBuffer(std::move(buffer->shmem));
bufferHandles.AppendElement(std::move(buffer->handle));
// Wait unless we detect the reading side has closed. while (!mHelpers->ReaderClosed() && mHeader->readerState != State::Failed) { if (mWriterSemaphore->Wait(Some(TimeDuration::FromMilliseconds(100)))) {
MOZ_ASSERT(mHeader->processedCount >= aCheckpoint); returntrue;
}
}
// Either the reader has failed or we're stopping writing for some other // reason (e.g. shutdown), so mark us as failed so the reader is aware.
mHeader->writerState = State::Failed; returnfalse;
}
gfx::ContiguousBuffer& CanvasDrawEventRecorder::GetContiguousBuffer(
size_t aSize) { if (!mCurrentBuffer.IsValid()) { // If the current buffer is invalid then we've already failed previously.
MOZ_ASSERT(mHeader->writerState == State::Failed); return mCurrentBuffer;
}
// We make sure that our buffer can hold aSize + 1 to ensure we always have // room for the end of buffer event.
// Check if there is enough room is our current buffer. if (mCurrentBuffer.SizeRemaining() > aSize) { return mCurrentBuffer;
}
bool useRecycledBuffer = false; if (mRecycledBuffers.front().Capacity() > aSize) { // The recycled buffer is big enough, check if it is free. if (mRecycledBuffers.front().eventCount <= mHeader->processedCount) {
useRecycledBuffer = true;
} elseif (mRecycledBuffers.size() >= mMaxDefaultBuffers) { // We've hit he max number of buffers, wait for the next one to be free. // We wait for (eventCount - 1), as we check and signal in the translator // during the play event, before the processedCount has been updated.
useRecycledBuffer = true; if (!WaitForCheckpoint(mRecycledBuffers.front().eventCount - 1)) { // The wait failed or we're shutting down, just return an empty buffer.
mCurrentBuffer = CanvasBuffer(); return mCurrentBuffer;
}
}
}
if (useRecycledBuffer) { // Only queue default size buffers for recycling. if (mCurrentBuffer.Capacity() == mDefaultBufferSize) {
WriteInternalEvent(RECYCLE_BUFFER);
mRecycledBuffers.emplace(std::move(mCurrentBuffer.shmem),
mHeader->eventCount);
} else {
WriteInternalEvent(DROP_BUFFER);
}
// If we have more than one recycled buffers free a configured number of // times in a row then drop one. if (mRecycledBuffers.size() > 1 &&
mRecycledBuffers.front().eventCount < mHeader->processedCount) { if (--mDropBufferOnZero == 0) {
WriteInternalEvent(DROP_BUFFER);
mCurrentBuffer =
CanvasBuffer(std::move(mRecycledBuffers.front().shmem));
mRecycledBuffers.pop();
mDropBufferOnZero = 1;
}
} else {
mDropBufferOnZero = mDropBufferLimit;
}
return mCurrentBuffer;
}
// We don't have a buffer free or it is not big enough, so create a new one.
WriteInternalEvent(PAUSE_TRANSLATION);
// Only queue default size buffers for recycling. if (mCurrentBuffer.Capacity() == mDefaultBufferSize) {
mRecycledBuffers.emplace(std::move(mCurrentBuffer.shmem),
mHeader->eventCount);
}
void CanvasDrawEventRecorder::DropFreeBuffers() { while (mRecycledBuffers.size() > 1 &&
mRecycledBuffers.front().eventCount < mHeader->processedCount) { // If we encountered an error, we may have invalidated mCurrentBuffer in // GetContiguousBuffer. No need to write the DROP_BUFFER event. if (mCurrentBuffer.IsValid()) {
WriteInternalEvent(DROP_BUFFER);
}
mCurrentBuffer = CanvasBuffer(std::move(mRecycledBuffers.front().shmem));
mRecycledBuffers.pop();
}
void CanvasDrawEventRecorder::CheckAndSignalReader() { do { switch (mHeader->readerState) { case State::Processing: case State::Paused: case State::Failed: return; case State::AboutToWait: // The reader is making a decision about whether to wait. So, we must // wait until it has decided to avoid races. Check if the reader is // closed to avoid hangs. if (mHelpers->ReaderClosed()) { return;
} continue; case State::Waiting: if (mHeader->processedCount < mHeader->eventCount) { // We have to use compareExchange here because the reader can change // from Waiting to Stopped. if (mHeader->readerState.compareExchange(State::Waiting,
State::Processing)) {
mReaderSemaphore->Signal(); return;
}
MOZ_ASSERT(mHeader->readerState == State::Stopped); continue;
} return; case State::Stopped: if (mHeader->processedCount < mHeader->eventCount) {
mHeader->readerState = State::Processing; if (!mHelpers->RestartReader()) {
mHeader->writerState = State::Failed;
}
} return; default:
MOZ_ASSERT_UNREACHABLE("Invalid waiting state."); return;
}
} while (true);
}
{ auto lockedPendingDeletions = mPendingDeletions.Lock();
mWorkerRef = nullptr;
}
}
void CanvasDrawEventRecorder::QueueProcessPendingDeletionsLocked(
RefPtr<CanvasDrawEventRecorder>&& aRecorder) { if (!mWorkerRef) {
MOZ_RELEASE_ASSERT(
!mIsOnWorker, "QueueProcessPendingDeletionsLocked called after worker shutdown!");
class ProcessPendingRunnable final : public dom::MainThreadWorkerRunnable { public: explicit ProcessPendingRunnable(RefPtr<CanvasDrawEventRecorder>&& aRecorder)
: dom::MainThreadWorkerRunnable("ProcessPendingRunnable"),
mRecorder(std::move(aRecorder)) {}
auto task = MakeRefPtr<ProcessPendingRunnable>(std::move(aRecorder)); if (NS_WARN_IF(!task->Dispatch(mWorkerRef->Private()))) {
MOZ_CRASH("ProcessPendingRunnable leaked!");
}
}
void CanvasDrawEventRecorder::QueueProcessPendingDeletions(
RefPtr<CanvasDrawEventRecorder>&& aRecorder) { auto lockedPendingDeletions = mPendingDeletions.Lock(); if (lockedPendingDeletions->empty()) { // We raced to handle the deletions, and something got there first. return;
}
{ auto lockedPendingDeletions = mPendingDeletions.Lock(); bool wasEmpty = lockedPendingDeletions->empty();
lockedPendingDeletions->emplace_back(std::move(aPendingDeletion));
MOZ_RELEASE_ASSERT(!mIsOnWorker || mWorkerRef, "AddPendingDeletion called after worker shutdown!");
// If we are not on the owning thread, we must queue an event to run the // deletions, if we transitioned from empty to non-empty. if ((mWorkerRef && !mWorkerRef->Private()->IsOnCurrentThread()) ||
(!mWorkerRef && !NS_IsMainThread())) { if (wasEmpty) {
RefPtr<CanvasDrawEventRecorder> self(this);
QueueProcessPendingDeletionsLocked(std::move(self));
} return;
}
// Otherwise, we can just run all of them right now.
pendingDeletions.swap(*lockedPendingDeletions);
}
for (constauto& pendingDeletion : pendingDeletions) {
pendingDeletion();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.