diff --git a/image/src/Decoder.h b/image/src/Decoder.h index f7b7b47a817..9f93e18c57e 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -76,7 +76,7 @@ public: void FlushInvalidations(); // We're not COM-y, so we don't get refcounts by default - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder) + NS_INLINE_DECL_REFCOUNTING(Decoder) /* * State. @@ -97,11 +97,6 @@ public: mSynchronous = aSynchronous; } - bool IsSynchronous() const - { - return mSynchronous; - } - void SetObserver(imgDecoderObserver* aObserver) { MOZ_ASSERT(aObserver); diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 10d071cdb2e..d515d93d5aa 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -16,14 +16,9 @@ #include "nsAutoPtr.h" #include "nsStringStream.h" #include "prenv.h" -#include "prsystem.h" #include "ImageContainer.h" #include "Layers.h" #include "nsPresContext.h" -#include "nsThread.h" -#include "nsIThreadPool.h" -#include "nsXPCOMCIDInternal.h" -#include "nsIObserverService.h" #include "nsPNGDecoder.h" #include "nsGIFDecoder2.h" @@ -35,7 +30,6 @@ #include "gfxContext.h" -#include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "mozilla/StandardInteger.h" #include "mozilla/Telemetry.h" @@ -76,8 +70,6 @@ static uint32_t gMaxMSBeforeYield = 0; static bool gHQDownscaling = false; // This is interpreted as a floating-point value / 1000 static uint32_t gHQDownscalingMinFactor = 1000; -static bool gMultithreadedDecoding = true; -static int32_t gDecodingThreadLimit = -1; // The maximum number of times any one RasterImage was decoded. This is only // used for statistics. @@ -94,10 +86,6 @@ InitPrefCaches() "image.high_quality_downscaling.enabled", false); Preferences::AddUintVarCache(&gHQDownscalingMinFactor, "image.high_quality_downscaling.min_factor", 1000); - Preferences::AddBoolVarCache(&gMultithreadedDecoding, - "image.multithreaded_decoding.enabled", true); - Preferences::AddIntVarCache(&gDecodingThreadLimit, - "image.multithreaded_decoding.limit", -1); } /* We define our own error checking macros here for 2 reasons: @@ -361,14 +349,14 @@ private: namespace mozilla { namespace image { -/* static */ StaticRefPtr RasterImage::DecodePool::sSingleton; +/* static */ StaticRefPtr RasterImage::DecodeWorker::sSingleton; static nsCOMPtr sScaleWorkerThread = nullptr; #ifndef DEBUG -NS_IMPL_THREADSAFE_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties) +NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties) #else -NS_IMPL_THREADSAFE_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, - imgIContainerDebug) +NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, + imgIContainerDebug) #endif //****************************************************************************** @@ -380,14 +368,12 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker, mAnim(nullptr), mLoopCount(-1), mLockCount(0), + mDecoder(nullptr), + mBytesDecoded(0), mDecodeCount(0), #ifdef DEBUG mFramesNotified(0), #endif - mDecodingMutex("RasterImage"), - mDecoder(nullptr), - mBytesDecoded(0), - mInDecoder(false), mHasSize(false), mDecodeOnDraw(false), mMultipart(false), @@ -395,6 +381,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker, mHasSourceData(false), mDecoded(false), mHasBeenDecoded(false), + mInDecoder(false), mAnimationFinished(false), mFinishing(false), mInUpdateImageContainer(false), @@ -431,8 +418,7 @@ RasterImage::~RasterImage() if (mDecoder) { // Kill off our decode request, if it's pending. (If not, this call is // harmless.) - MutexAutoLock lock(mDecodingMutex); - DecodePool::StopDecoding(this); + DecodeWorker::Singleton()->StopDecoding(this); mDecoder = nullptr; // Unlock the last frame (if we have any). Our invariant is that, while we @@ -466,7 +452,7 @@ RasterImage::Initialize() // Create our singletons now, so we don't have to worry about what thread // they're created on. - DecodePool::Singleton(); + DecodeWorker::Singleton(); } nsresult @@ -793,7 +779,7 @@ RasterImage::GetHeight(int32_t *aHeight) *aHeight = mSize.height; return NS_OK; } - + //****************************************************************************** /* [noscript] readonly attribute nsSize intrinsicSize; */ NS_IMETHODIMP @@ -1391,8 +1377,6 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags) nsresult RasterImage::SetSize(int32_t aWidth, int32_t aHeight) { - MOZ_ASSERT(NS_IsMainThread()); - if (mError) return NS_ERROR_FAILURE; @@ -1548,8 +1532,6 @@ RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult) nsresult RasterImage::DecodingComplete() { - MOZ_ASSERT(NS_IsMainThread()); - if (mError) return NS_ERROR_FAILURE; @@ -1678,8 +1660,6 @@ RasterImage::SetLoopCount(int32_t aLoopCount) nsresult RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) { - MutexAutoLock lock(mDecodingMutex); - if (mError) return NS_ERROR_FAILURE; @@ -1741,9 +1721,6 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) mInDecoder = true; mDecoder->FlushInvalidations(); mInDecoder = false; - - rv = FinishedSomeDecoding(); - CONTAINER_ENSURE_SUCCESS(rv); } // Otherwise, we're storing data in the source buffer @@ -1754,8 +1731,10 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount) if (!newElem) return NS_ERROR_OUT_OF_MEMORY; + // If there's a decoder open, that means we want to do more decoding. + // Wake up the worker. if (mDecoder) { - DecodePool::Singleton()->RequestDecode(this); + DecodeWorker::Singleton()->RequestDecode(this); } } @@ -1799,8 +1778,6 @@ get_header_str (char *buf, char *data, size_t data_len) nsresult RasterImage::DoImageDataComplete() { - MOZ_ASSERT(NS_IsMainThread()); - if (mError) return NS_ERROR_FAILURE; @@ -1809,28 +1786,27 @@ RasterImage::DoImageDataComplete() return NS_OK; mHasSourceData = true; + // This call should come straight from necko - no reentrancy allowed + NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!"); + // If there's a decoder open, synchronously decode the beginning of the image // to check for errors and get the image's size. (If we already have the // image's size, this does nothing.) Then kick off an async decode of the // rest of the image. if (mDecoder) { - nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); + nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this); CONTAINER_ENSURE_SUCCESS(rv); } - { - MutexAutoLock lock(mDecodingMutex); - - // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker - // finish decoding this image. - if (mDecoder) { - DecodePool::Singleton()->RequestDecode(this); - } - - // Free up any extra space in the backing buffer - mSourceData.Compact(); + // If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker + // finish decoding this image. + if (mDecoder) { + DecodeWorker::Singleton()->RequestDecode(this); } + // Free up any extra space in the backing buffer + mSourceData.Compact(); + // Log header information if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) { char buf[9]; @@ -1869,10 +1845,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bo } // We just recorded OnStopRequest; we need to inform our listeners. - { - MutexAutoLock lock(mDecodingMutex); - FinishedSomeDecoding(); - } + FinishedSomeDecoding(); return finalStatus; } @@ -1900,8 +1873,6 @@ RasterImage::OnImageDataAvailable(nsIRequest*, nsresult RasterImage::OnNewSourceData() { - MOZ_ASSERT(NS_IsMainThread()); - nsresult rv; if (mError) @@ -2457,8 +2428,6 @@ RasterImage::GetKeys(uint32_t *count, char ***keys) void RasterImage::Discard(bool force) { - MOZ_ASSERT(NS_IsMainThread()); - // We should be ok for discard NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!"); @@ -2684,7 +2653,7 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent) // Kill off our decode request, if it's pending. (If not, this call is // harmless.) - DecodePool::StopDecoding(this); + DecodeWorker::Singleton()->StopDecoding(this); nsresult decoderStatus = decoder->GetDecoderError(); if (NS_FAILED(decoderStatus)) { @@ -2719,8 +2688,6 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent) nsresult RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount) { - mDecodingMutex.AssertCurrentThreadOwns(); - // We should have a decoder NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!"); @@ -2784,8 +2751,6 @@ RasterImage::StartDecoding() NS_IMETHODIMP RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) { - MOZ_ASSERT(NS_IsMainThread()); - nsresult rv; if (mError) @@ -2816,7 +2781,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) // If we have a size decoder open, make sure we get the size if (mDecoder && mDecoder->IsSizeDecode()) { - nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); + nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this); CONTAINER_ENSURE_SUCCESS(rv); // If we didn't get the size out of the image, we won't until we get more @@ -2833,8 +2798,6 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) if (mDecoded) return NS_OK; - MutexAutoLock lock(mDecodingMutex); - // If we have a size decode open, interrupt it and shut it down; or if // the decoder has different flags than what we need if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) { @@ -2842,7 +2805,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) CONTAINER_ENSURE_SUCCESS(rv); } - // If we don't have a decoder, create one + // If we don't have a decoder, create one if (!mDecoder) { rv = InitDecoder(/* aDoSizeDecode = */ false); CONTAINER_ENSURE_SUCCESS(rv); @@ -2853,13 +2816,6 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) MOZ_ASSERT(mDecoder); } - // If we're waiting for decode work to be notified, go ahead and do that. - if (mDecodeRequest && - mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { - nsresult rv = FinishedSomeDecoding(); - CONTAINER_ENSURE_SUCCESS(rv); - } - // If we've read all the data we have, we're done if (mHasSourceData && mBytesDecoded == mSourceData.Length()) return NS_OK; @@ -2871,7 +2827,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get()); mDecoder->SetSynchronous(true); - DecodePool::Singleton()->DecodeABitOf(this); + DecodeWorker::Singleton()->DecodeABitOf(this); // DecodeABitOf can destroy mDecoder. if (mDecoder) { @@ -2880,12 +2836,10 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) return NS_OK; } - if (!mDecoded) { - // If we get this far, dispatch the worker. We do this instead of starting - // any immediate decoding to guarantee that all our decode notifications are - // dispatched asynchronously, and to ensure we stay responsive. - DecodePool::Singleton()->RequestDecode(this); - } + // If we get this far, dispatch the worker. We do this instead of starting + // any immediate decoding to guarantee that all our decode notifications are + // dispatched asynchronously, and to ensure we stay responsive. + DecodeWorker::Singleton()->RequestDecode(this); return NS_OK; } @@ -2894,16 +2848,6 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) nsresult RasterImage::SyncDecode() { - MutexAutoLock imgLock(mDecodingMutex); - - if (mDecodeRequest) { - // If the image is waiting for decode work to be notified, go ahead and do that. - if (mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { - nsresult rv = FinishedSomeDecoding(); - CONTAINER_ENSURE_SUCCESS(rv); - } - } - nsresult rv; SAMPLE_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());; @@ -2916,7 +2860,7 @@ RasterImage::SyncDecode() // If we have a size decoder open, make sure we get the size if (mDecoder && mDecoder->IsSizeDecode()) { - nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this); + nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this); CONTAINER_ENSURE_SUCCESS(rv); // If we didn't get the size out of the image, we won't until we get more @@ -2972,9 +2916,6 @@ RasterImage::SyncDecode() if (mDecoder) { mDecoder->SetSynchronous(false); - - // If our decoder's still open, there's still work to be done. - DecodePool::Singleton()->RequestDecode(this); } // All good if no errors! @@ -3185,7 +3126,11 @@ RasterImage::Draw(gfxContext *aContext, // We use !mDecoded && mHasSourceData to mean discarded. if (!mDecoded && mHasSourceData) { - mDrawStartTime = TimeStamp::Now(); + mDrawStartTime = TimeStamp::Now(); + + // We're drawing this image, so indicate that we should decode it as soon + // as possible. + DecodeWorker::Singleton()->MarkAsASAP(this); } // If a synchronous draw is requested, flush anything that might be sitting around @@ -3259,7 +3204,6 @@ RasterImage::UnlockImage() PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG, ("RasterImage[0x%p] canceling decode because image " "is now unlocked.", this)); - MutexAutoLock lock(mDecodingMutex); FinishedSomeDecoding(eShutdownIntent_NotNeeded); ForceDiscard(); return NS_OK; @@ -3294,8 +3238,6 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes) // We should have a decoder if we get here NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!"); - mDecodingMutex.AssertCurrentThreadOwns(); - // First, if we've just been called because we allocated a frame on the main // thread, let the decoder deal with the data it set aside at that time by // passing it a null buffer. @@ -3313,7 +3255,7 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes) // write the proper amount of data uint32_t bytesToDecode = std::min(aMaxBytes, - mSourceData.Length() - mBytesDecoded); + mSourceData.Length() - mBytesDecoded); nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded, bytesToDecode); @@ -3369,7 +3311,6 @@ RasterImage::DoError() // If we're mid-decode, shut down the decoder. if (mDecoder) { - MutexAutoLock lock(mDecodingMutex); FinishedSomeDecoding(eShutdownIntent_Error); } @@ -3442,8 +3383,6 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D { MOZ_ASSERT(NS_IsMainThread()); - mDecodingMutex.AssertCurrentThreadOwns(); - nsRefPtr request; if (aRequest) { request = aRequest; @@ -3505,140 +3444,135 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D } } - imgStatusTracker::StatusDiff diff; + // Then, tell the observers what happened in the decoder. + // If we have no request, we have not yet created a decoder, but we still + // need to send out notifications. if (request) { - diff = image->mStatusTracker->CalculateAndApplyDifference(request->mStatusTracker); + image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker); + } else { + image->mStatusTracker->SyncNotifyDecodeState(); } - { - // Notifications can't go out with the decoding lock held. - MutexAutoUnlock unlock(mDecodingMutex); + // If we were a size decode and a full decode was requested, now's the time. + if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) { + image->mWantFullDecode = false; - // Then, tell the observers what happened in the decoder. - // If we have no request, we have not yet created a decoder, but we still - // need to send out notifications. - if (request) { - image->mStatusTracker->SyncNotifyDifference(diff); + // If we're not meant to be storing source data and we just got the size, + // we need to synchronously flush all the data we got to a full decoder. + // When that decoder is shut down, we'll also clear our source data. + if (!image->StoringSourceData()) { + rv = image->SyncDecode(); } else { - image->mStatusTracker->SyncNotifyDecodeState(); + rv = image->RequestDecode(); } - - // If we were a size decode and a full decode was requested, now's the time. - if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) { - image->mWantFullDecode = false; - - // If we're not meant to be storing source data and we just got the size, - // we need to synchronously flush all the data we got to a full decoder. - // When that decoder is shut down, we'll also clear our source data. - if (!image->StoringSourceData()) { - rv = image->SyncDecode(); - } else { - rv = image->RequestDecode(); - } - } - } return rv; } -NS_IMPL_THREADSAFE_ISUPPORTS1(RasterImage::DecodePool, - nsIObserver) - -/* static */ RasterImage::DecodePool* -RasterImage::DecodePool::Singleton() +/* static */ RasterImage::DecodeWorker* +RasterImage::DecodeWorker::Singleton() { if (!sSingleton) { - MOZ_ASSERT(NS_IsMainThread()); - sSingleton = new DecodePool(); + sSingleton = new DecodeWorker(); ClearOnShutdown(&sSingleton); } return sSingleton; } -RasterImage::DecodePool::DecodePool() +RasterImage::DecodeWorker::~DecodeWorker() { - if (gMultithreadedDecoding) { - mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); - if (mThreadPool) { - mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder")); - if (gDecodingThreadLimit <= 0) { - mThreadPool->SetThreadLimit(std::max(PR_GetNumberOfProcessors() - 1, 1)); - } else { - mThreadPool->SetThreadLimit(static_cast(gDecodingThreadLimit)); - } + MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!"); - nsCOMPtr obsSvc = mozilla::services::GetObserverService(); - if (obsSvc) { - obsSvc->AddObserver(this, "xpcom-shutdown-threads", false); - } - } - } -} + // Shut down all the decoders since we're going away. + DecodeRequest* request = mASAPDecodeRequests.getFirst(); + while (request) { + request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded); -RasterImage::DecodePool::~DecodePool() -{ - MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!"); -} - -NS_IMETHODIMP -RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic, - const PRUnichar *data) -{ - NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops"); - - if (mThreadPool) { - mThreadPool->Shutdown(); - mThreadPool = nullptr; + request = request->getNext(); } - return NS_OK; + request = mNormalDecodeRequests.getFirst(); + while (request) { + request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded); + + request = request->getNext(); + } } void -RasterImage::DecodePool::RequestDecode(RasterImage* aImg) +RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg) +{ + // We can be marked as ASAP before we've been asked to decode. If we are, + // create the request so we have somewhere to write down our status. + if (!aImg->mDecodeRequest) { + aImg->mDecodeRequest = new DecodeRequest(aImg); + } + + DecodeRequest* request = aImg->mDecodeRequest; + + // If we're already an ASAP request, there's nothing to do here. + if (request->mIsASAP) { + return; + } + + request->mIsASAP = true; + + if (request->isInList()) { + // If the decode request is in a list, it must be in the normal decode + // requests list -- if it had been in the ASAP list, then mIsASAP would + // have been true above. Move the request to the ASAP list. + request->removeFrom(mNormalDecodeRequests); + mASAPDecodeRequests.insertBack(request); + + // Since request is in a list, one of the decode worker's lists is + // non-empty, so the worker should be pending in the event loop. + // + // (Note that this invariant only holds while we are not in Run(), because + // DecodeSomeOfImage adds requests to the decode worker using + // AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call + // EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP + // from within DecodeWorker::Run.) + MOZ_ASSERT(mPendingInEventLoop); + } +} + +void +RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode) +{ + if (aRequest->isInList()) { + // The image is already in our list of images to decode, so we don't have + // to do anything here. + return; + } + + aRequest->mBytesToDecode = bytesToDecode; + + if (aRequest->mIsASAP) { + mASAPDecodeRequests.insertBack(aRequest); + } else { + mNormalDecodeRequests.insertBack(aRequest); + } +} + +void +RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg) { MOZ_ASSERT(aImg->mDecoder); - MOZ_ASSERT(NS_IsMainThread()); - aImg->mDecodingMutex.AssertCurrentThreadOwns(); // If we're currently waiting on a new frame for this image, we can't do any // decoding. if (!aImg->mDecoder->NeedsNewFrame()) { - // No matter whether this is currently being decoded, we need to update the - // number of bytes we want it to decode. - aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded; - - if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING || - aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) { - // The image is already in our list of images to decode, or currently being - // decoded, so we don't have to do anything else. - return; - } - - aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING; - nsRefPtr job = new DecodeJob(aImg->mDecodeRequest, aImg); - if (!gMultithreadedDecoding || !mThreadPool) { - NS_DispatchToMainThread(job); - } else { - mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL); - } + AddDecodeRequest(aImg->mDecodeRequest, aImg->mSourceData.Length() - aImg->mBytesDecoded); + EnsurePendingInEventLoop(); } } void -RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg) +RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg) { MOZ_ASSERT(NS_IsMainThread()); - aImg->mDecodingMutex.AssertCurrentThreadOwns(); - - if (aImg->mDecodeRequest) { - // If the image is waiting for decode work to be notified, go ahead and do that. - if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { - aImg->FinishedSomeDecoding(); - } - } DecodeSomeOfImage(aImg); @@ -3660,90 +3594,98 @@ RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg) } } -/* static */ void -RasterImage::DecodePool::StopDecoding(RasterImage* aImg) +void +RasterImage::DecodeWorker::EnsurePendingInEventLoop() { - aImg->mDecodingMutex.AssertCurrentThreadOwns(); + if (!mPendingInEventLoop) { + mPendingInEventLoop = true; + NS_DispatchToCurrentThread(this); + } +} +void +RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg) +{ // If we haven't got a decode request, we're not currently decoding. (Having // a decode request doesn't imply we *are* decoding, though.) if (aImg->mDecodeRequest) { - aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_INACTIVE; + if (aImg->mDecodeRequest->isInList()) { + aImg->mDecodeRequest->remove(); + } } } NS_IMETHODIMP -RasterImage::DecodePool::DecodeJob::Run() +RasterImage::DecodeWorker::Run() { - MutexAutoLock imglock(mImage->mDecodingMutex); + // We just got called back by the event loop; therefore, we're no longer + // pending. + mPendingInEventLoop = false; - // If we were interrupted, we shouldn't do any work. - if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) { - return NS_OK; + TimeStamp eventStart = TimeStamp::Now(); + + // Now decode until we either run out of time or run out of images. + do { + // Try to get an ASAP request to handle. If there isn't one, try to get a + // normal request. If no normal request is pending either, then we're done + // here. + DecodeRequest* request = mASAPDecodeRequests.popFirst(); + if (!request) + request = mNormalDecodeRequests.popFirst(); + if (!request) + break; + + // This has to be a strong pointer, because DecodeSomeOfImage may destroy + // image->mDecoder, which may be holding the only other reference to image. + nsRefPtr image = request->mImage; + uint32_t oldFrameCount = image->mDecoder->GetFrameCount(); + uint32_t oldByteCount = image->mBytesDecoded; + + DecodeSomeOfImage(image, DECODE_TYPE_NORMAL, request->mBytesToDecode); + + uint32_t bytesDecoded = image->mBytesDecoded - oldByteCount; + + // If the decoder needs a new frame, enqueue an event to get it; that event + // will enqueue another decode request when it's done. + if (image->mDecoder && image->mDecoder->NeedsNewFrame()) { + FrameNeededWorker::GetNewFrame(image); + } + + // If we aren't yet finished decoding and we have more data in hand, add + // this request to the back of the list. + else if (image->mDecoder && + !image->mError && + !image->IsDecodeFinished() && + bytesDecoded < request->mBytesToDecode) { + AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded); + + // If we have a new frame, let everybody know about it. + if (image->mDecoder->GetFrameCount() != oldFrameCount) { + DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request); + } + } else { + // Nothing more for us to do - let everyone know what happened. + DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request); + } + + } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield); + + // If decode requests are pending, re-post ourself to the event loop. + if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) { + EnsurePendingInEventLoop(); } - // If someone came along and synchronously decoded us, there's nothing for us to do. - if (!mRequest->mAllocatedNewFrame && (!mImage->mDecoder || mImage->IsDecodeFinished() || - mRequest->mRequestStatus != DecodeRequest::REQUEST_PENDING)) { - return NS_OK; - } - - mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE; - - uint32_t oldByteCount = mImage->mBytesDecoded; - - DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES; - - // Multithreaded decoding can be disabled. If we've done so, we don't want to - // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage. - if (NS_IsMainThread()) { - type = DECODE_TYPE_UNTIL_TIME; - } - - DecodePool::Singleton()->DecodeSomeOfImage(mImage, type, mRequest->mBytesToDecode); - - uint32_t bytesDecoded = mImage->mBytesDecoded - oldByteCount; - - mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE; - - // If the decoder needs a new frame, enqueue an event to get it; that event - // will enqueue another decode request when it's done. - if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) { - FrameNeededWorker::GetNewFrame(mImage); - } - // If we aren't yet finished decoding and we have more data in hand, add - // this request to the back of the list. - else if (mImage->mDecoder && - !mImage->mError && - !mImage->IsDecodeFinished() && - bytesDecoded < mRequest->mBytesToDecode) { - DecodePool::Singleton()->RequestDecode(mImage); - } else { - // Nothing more for us to do - let everyone know what happened. - DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); - } + Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY_US, + uint32_t((TimeStamp::Now() - eventStart).ToMicroseconds())); return NS_OK; } nsresult -RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg) +RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg) { MOZ_ASSERT(NS_IsMainThread()); - MutexAutoLock imgLock(aImg->mDecodingMutex); - - if (aImg->mDecodeRequest) { - // If the image is waiting for decode work to be notified, go ahead and do that. - if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) { - nsresult rv = aImg->FinishedSomeDecoding(); - if (NS_FAILED(rv)) { - aImg->DoError(); - return rv; - } - } - } - nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE); if (NS_FAILED(rv)) { return rv; @@ -3761,13 +3703,12 @@ RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg) } nsresult -RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg, - DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */, - uint32_t bytesToDecode /* = 0 */) +RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg, + DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */, + uint32_t bytesToDecode /* = 0 */) { NS_ABORT_IF_FALSE(aImg->mInitialized, "Worker active for uninitialized container!"); - aImg->mDecodingMutex.AssertCurrentThreadOwns(); // If an error is flagged, it probably happened while we were waiting // in the event queue. @@ -3779,17 +3720,9 @@ RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg, if (!aImg->mDecoder || aImg->mDecoded) return NS_OK; - // If we're doing synchronous decodes, and we're waiting on a new frame for - // this image, get it now. - if (aImg->mDecoder->IsSynchronous() && aImg->mDecoder->NeedsNewFrame()) { - MOZ_ASSERT(NS_IsMainThread()); - - aImg->mDecoder->AllocateFrame(); - aImg->mDecodeRequest->mAllocatedNewFrame = true; - } - - // If we're not synchronous, we can't allocate a frame right now. - else if (aImg->mDecoder->NeedsNewFrame()) { + // If we're currently waiting on a new frame for this image, we can't do any + // decoding right now. + if (aImg->mDecoder->NeedsNewFrame()) { return NS_OK; } @@ -3840,7 +3773,7 @@ RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg, // Yield if we've been decoding for too long. We check this _after_ decoding // a chunk to ensure that we don't yield without doing any decoding. - if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline) + if (TimeStamp::Now() >= deadline) break; } @@ -3885,8 +3818,6 @@ RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeReques void RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request) { - image->mDecodingMutex.AssertCurrentThreadOwns(); - nsCOMPtr worker = new DecodeDoneWorker(image, request); NS_DispatchToMainThread(worker); } @@ -3894,15 +3825,12 @@ RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, De NS_IMETHODIMP RasterImage::DecodeDoneWorker::Run() { - MOZ_ASSERT(NS_IsMainThread()); - MutexAutoLock lock(mImage->mDecodingMutex); - mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest); // If we didn't finish decoding yet, try again if (mImage->mDecoder && !mImage->IsDecodeFinished() && mImage->mSourceData.Length() > mImage->mBytesDecoded) { - DecodePool::Singleton()->RequestDecode(mImage); + DecodeWorker::Singleton()->RequestDecode(mImage); } return NS_OK; @@ -3923,7 +3851,6 @@ RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image) NS_IMETHODIMP RasterImage::FrameNeededWorker::Run() { - MutexAutoLock lock(mImage->mDecodingMutex); nsresult rv = NS_OK; // If we got a synchronous decode in the mean time, we don't need to do @@ -3934,8 +3861,7 @@ RasterImage::FrameNeededWorker::Run() } if (NS_SUCCEEDED(rv) && mImage->mDecoder) { - // By definition, we're not done decoding, so enqueue us for more decoding. - DecodePool::Singleton()->RequestDecode(mImage); + DecodeWorker::Singleton()->RequestDecode(mImage); } return NS_OK; diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 3634ce0937d..7f23c3f932d 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -18,6 +18,7 @@ #define mozilla_imagelib_RasterImage_h_ #include "Image.h" +#include "nsCOMArray.h" #include "nsCOMPtr.h" #include "imgIContainer.h" #include "nsIProperties.h" @@ -27,20 +28,18 @@ #include "imgFrame.h" #include "nsThreadUtils.h" #include "DiscardTracker.h" -#include "nsISupportsImpl.h" #include "mozilla/TimeStamp.h" #include "mozilla/Telemetry.h" #include "mozilla/LinkedList.h" #include "mozilla/StaticPtr.h" #include "mozilla/WeakPtr.h" -#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" #include "gfx2DGlue.h" #ifdef DEBUG #include "imgIContainerDebug.h" #endif class nsIInputStream; -class nsIThreadPool; #define NS_RASTERIMAGE_CID \ { /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \ @@ -388,23 +387,25 @@ private: }; /** + * DecodeWorker keeps a linked list of DecodeRequests to keep track of the + * images it needs to decode. + * * Each RasterImage has a pointer to one or zero heap-allocated * DecodeRequests. */ - struct DecodeRequest + struct DecodeRequest : public LinkedListElement, + public RefCounted { DecodeRequest(RasterImage* aImage) : mImage(aImage) , mBytesToDecode(0) - , mRequestStatus(REQUEST_INACTIVE) , mChunkCount(0) , mAllocatedNewFrame(false) + , mIsASAP(false) { mStatusTracker = aImage->mStatusTracker->CloneForRecording(); } - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest) - // The status tracker that is associated with a given decode request, to // ensure their lifetimes are linked. nsRefPtr mStatusTracker; @@ -413,15 +414,6 @@ private: uint32_t mBytesToDecode; - enum DecodeRequestStatus - { - REQUEST_INACTIVE, - REQUEST_PENDING, - REQUEST_ACTIVE, - REQUEST_WORK_DONE, - REQUEST_STOPPED - } mRequestStatus; - /* Keeps track of how much time we've burned decoding this particular decode * request. */ TimeDuration mDecodeTime; @@ -432,31 +424,35 @@ private: /* True if a new frame has been allocated, but DecodeSomeData hasn't yet * been called to flush data to it */ bool mAllocatedNewFrame; + + /* True if we need to handle this decode as soon as possible. */ + bool mIsASAP; }; /* - * DecodePool is a singleton class we use when decoding large images. + * DecodeWorker is a singleton class we use when decoding large images. * * When we wish to decode an image larger than - * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode() + * image.mem.max_bytes_for_sync_decode, we call DecodeWorker::RequestDecode() * for the image. This adds the image to a queue of pending requests and posts - * the DecodePool singleton to the event queue, if it's not already pending + * the DecodeWorker singleton to the event queue, if it's not already pending * there. * - * When the DecodePool is run from the event queue, it decodes the image (and + * When the DecodeWorker is run from the event queue, it decodes the image (and * all others it's managing) in chunks, periodically yielding control back to * the event loop. + * + * An image being decoded may have one of two priorities: normal or ASAP. ASAP + * images are always decoded before normal images. (We currently give ASAP + * priority to images which appear onscreen but are not yet decoded.) */ - class DecodePool : public nsIObserver + class DecodeWorker : public nsRunnable { public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - static DecodePool* Singleton(); + static DecodeWorker* Singleton(); /** - * Ask the DecodePool to asynchronously decode this image. + * Ask the DecodeWorker to asynchronously decode this image. */ void RequestDecode(RasterImage* aImg); @@ -467,13 +463,23 @@ private: void DecodeABitOf(RasterImage* aImg); /** - * Ask the DecodePool to stop decoding this image. Internally, we also + * Give this image ASAP priority; it will be decoded before all non-ASAP + * images. You can call MarkAsASAP before or after you call RequestDecode + * for the image, but if you MarkAsASAP before you call RequestDecode, you + * still need to call RequestDecode. + * + * StopDecoding() resets the image's ASAP flag. + */ + void MarkAsASAP(RasterImage* aImg); + + /** + * Ask the DecodeWorker to stop decoding this image. Internally, we also * call this function when we finish decoding an image. * - * Since the DecodePool keeps raw pointers to RasterImages, make sure you + * Since the DecodeWorker keeps raw pointers to RasterImages, make sure you * call this before a RasterImage is destroyed! */ - static void StopDecoding(RasterImage* aImg); + void StopDecoding(RasterImage* aImg); /** * Synchronously decode the beginning of the image until we run out of @@ -485,56 +491,56 @@ private: */ nsresult DecodeUntilSizeAvailable(RasterImage* aImg); - virtual ~DecodePool(); + NS_IMETHOD Run(); + + virtual ~DecodeWorker(); private: /* statics */ - static StaticRefPtr sSingleton; + static StaticRefPtr sSingleton; private: /* methods */ - DecodePool(); + DecodeWorker() + : mPendingInEventLoop(false) + {} + + /* Post ourselves to the event loop if we're not currently pending. */ + void EnsurePendingInEventLoop(); + + /* Add the given request to the appropriate list of decode requests, but + * don't ensure that we're pending in the event loop. */ + void AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode); enum DecodeType { - DECODE_TYPE_UNTIL_TIME, - DECODE_TYPE_UNTIL_SIZE, - DECODE_TYPE_UNTIL_DONE_BYTES + DECODE_TYPE_NORMAL, + DECODE_TYPE_UNTIL_SIZE }; /* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE, * decode until we have the image's size, then stop. If bytesToDecode is - * non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is - * UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded. - */ + * non-0, at most bytesToDecode bytes will be decoded. */ nsresult DecodeSomeOfImage(RasterImage* aImg, - DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME, + DecodeType aDecodeType = DECODE_TYPE_NORMAL, uint32_t bytesToDecode = 0); - /* A decode job dispatched to a thread pool by DecodePool. - */ - class DecodeJob : public nsRunnable - { - public: - DecodeJob(DecodeRequest* aRequest, RasterImage* aImg) - : mRequest(aRequest) - , mImage(aImg) - {} - - NS_IMETHOD Run(); - - private: - nsRefPtr mRequest; - nsRefPtr mImage; - }; + /* Create a new DecodeRequest suitable for doing some decoding and set it + * as aImg's mDecodeRequest. */ + void CreateRequestForImage(RasterImage* aImg); private: /* members */ - nsCOMPtr mThreadPool; + LinkedList mASAPDecodeRequests; + LinkedList mNormalDecodeRequests; + + /* True if we've posted ourselves to the event loop and expect Run() to + * be called sometime in the future. */ + bool mPendingInEventLoop; }; class DecodeDoneWorker : public nsRunnable { public: /** - * Called by the DecodePool with an image when it's done some significant + * Called by the DecodeWorker with an image when it's done some significant * portion of decoding that needs to be notified about. * * Ensures the decode state accumulated by the decoding process gets @@ -557,7 +563,7 @@ private: { public: /** - * Called by the DecodeJob with an image when it's been told by the + * Called by the DecodeWorker with an image when it's been told by the * decoder that it needs a new frame to be allocated on the main thread. * * Dispatches an event to do so, which will further dispatch a @@ -748,10 +754,16 @@ private: // data DiscardTracker::Node mDiscardTrackerNode; // Source data members + FallibleTArray mSourceData; nsCString mSourceDataMimeType; friend class DiscardTracker; + // Decoder and friends + nsRefPtr mDecoder; + nsRefPtr mDecodeRequest; + uint32_t mBytesDecoded; + // How many times we've decoded this image. // This is currently only used for statistics int32_t mDecodeCount; @@ -766,22 +778,6 @@ private: // data uint32_t mFramesNotified; #endif - // Below are the pieces of data that can be accessed on more than one thread - // at once, and hence need to be locked by mDecodingMutex. - - // BEGIN LOCKED MEMBER VARIABLES - mozilla::Mutex mDecodingMutex; - - FallibleTArray mSourceData; - - // Decoder and friends - nsRefPtr mDecoder; - nsRefPtr mDecodeRequest; - uint32_t mBytesDecoded; - // END LOCKED MEMBER VARIABLES - - bool mInDecoder; - // Boolean flags (clustered together to conserve space): bool mHasSize:1; // Has SetSize() been called? bool mDecodeOnDraw:1; // Decoding on draw? @@ -793,6 +789,7 @@ private: // data bool mDecoded:1; bool mHasBeenDecoded:1; + bool mInDecoder:1; // Whether the animation can stop, due to running out // of frames, or no more owning request diff --git a/image/src/VectorImage.cpp b/image/src/VectorImage.cpp index 334dd620050..67027e73bf3 100644 --- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -388,8 +388,7 @@ VectorImage::OnImageDataComplete(nsIRequest* aRequest, nsRefPtr clone = mStatusTracker->CloneForRecording(); imgDecoderObserver* observer = clone->GetDecoderObserver(); observer->OnStopRequest(aLastPart, finalStatus); - imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone); - mStatusTracker->SyncNotifyDifference(diff); + mStatusTracker->SyncAndSyncNotifyDifference(clone); } return finalStatus; } @@ -847,8 +846,7 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) nsRefPtr clone = mStatusTracker->CloneForRecording(); imgDecoderObserver* observer = clone->GetDecoderObserver(); observer->OnStartDecode(); - imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone); - mStatusTracker->SyncNotifyDifference(diff); + mStatusTracker->SyncAndSyncNotifyDifference(clone); } // Create a listener to wait until the SVG document is fully loaded, which @@ -935,8 +933,7 @@ VectorImage::OnSVGDocumentLoaded() observer->OnStopFrame(); observer->OnStopDecode(NS_OK); // Unblock page load. - imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone); - mStatusTracker->SyncNotifyDifference(diff); + mStatusTracker->SyncAndSyncNotifyDifference(clone); } EvaluateAnimation(); @@ -958,8 +955,7 @@ VectorImage::OnSVGDocumentError() // Unblock page load. observer->OnStopDecode(NS_ERROR_FAILURE); - imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone); - mStatusTracker->SyncNotifyDifference(diff); + mStatusTracker->SyncAndSyncNotifyDifference(clone); } } diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index d0c96269774..b2a33688b74 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -517,26 +517,24 @@ imgStatusTracker::SyncNotifyState(nsTObserverArray& proxies, } } -imgStatusTracker::StatusDiff -imgStatusTracker::CalculateAndApplyDifference(imgStatusTracker* other) +void +imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other) { - LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndCalculateDifference"); + LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndSyncNotifyDifference"); // We must not modify or notify for the start-load state, which happens from Necko callbacks. uint32_t loadState = mState & stateRequestStarted; - - StatusDiff diff; - diff.mDiffState = ~mState & other->mState & ~stateRequestStarted; - diff.mUnblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload); - diff.mFoundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR); + uint32_t diffState = ~mState & other->mState & ~stateRequestStarted; + bool unblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload); + bool foundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR); // Now that we've calculated the difference in state, synchronize our state // with the other tracker. // First, actually synchronize our state. - diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect); - mState |= diff.mDiffState | loadState; - if (diff.mUnblockedOnload) { + mInvalidRect = mInvalidRect.Union(other->mInvalidRect); + mState |= diffState | loadState; + if (unblockedOnload) { mState &= ~stateBlockingOnload; } mImageStatus = other->mImageStatus; @@ -554,21 +552,9 @@ imgStatusTracker::CalculateAndApplyDifference(imgStatusTracker* other) } } - // Reset the invalid rectangles for another go. - other->mInvalidRect.SetEmpty(); - mInvalidRect.SetEmpty(); + SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart); - return diff; -} - -void -imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff) -{ - LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference"); - - SyncNotifyState(mConsumers, !!mImage, diff.mDiffState, diff.mInvalidRect, mHadLastPart); - - if (diff.mUnblockedOnload) { + if (unblockedOnload) { nsTObserverArray::ForwardIterator iter(mConsumers); while (iter.HasMore()) { // Hold on to a reference to this proxy, since notifying the state can @@ -581,7 +567,11 @@ imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff) } } - if (diff.mFoundError) { + // Reset the invalid rectangles for another go. + other->mInvalidRect.SetEmpty(); + mInvalidRect.SetEmpty(); + + if (foundError) { FireFailureNotification(); } } diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index 4a8e38a861e..8f95b70908c 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -23,11 +23,11 @@ class Image; #include "mozilla/RefPtr.h" #include "nsCOMPtr.h" +#include "nsAutoPtr.h" #include "nsTObserverArray.h" #include "nsIRunnable.h" #include "nscore.h" #include "imgDecoderObserver.h" -#include "nsISupportsImpl.h" enum { stateRequestStarted = 1u << 0, @@ -51,11 +51,9 @@ enum { * and the notifications will be replayed to the proxy asynchronously. */ -class imgStatusTracker +class imgStatusTracker : public mozilla::RefCounted { public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker) - // aImage is the image that this status tracker will pass to the // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be // alive as long as this instance is, because we hold a weak reference to it. @@ -195,22 +193,7 @@ public: inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } imgStatusTracker* CloneForRecording(); - - struct StatusDiff - { - uint32_t mDiffState; - bool mUnblockedOnload; - bool mFoundError; - nsIntRect mInvalidRect; - }; - - // Calculate the difference between this and other, apply that difference to - // ourselves, and return it for passing to SyncNotifyDifference. - StatusDiff CalculateAndApplyDifference(imgStatusTracker* other); - - // Notify for the difference found in CalculateAndApplyDifference. No - // decoding locks may be held. - void SyncNotifyDifference(StatusDiff diff); + void SyncAndSyncNotifyDifference(imgStatusTracker* other); nsIntRect GetInvalidRect() const { return mInvalidRect; } diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index b07e6e9797b..178fda542fb 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3870,14 +3870,6 @@ pref("image.mem.max_ms_before_yield", 5); // might keep around more than this, but we'll try to get down to this value). pref("image.mem.max_decoded_image_kb", 51200); -// Whether we decode images on multiple background threads rather than the -// foreground thread. -pref("image.multithreaded_decoding.enabled", true); - -// How many threads we'll use for multithreaded decoding. If < 0, will be -// automatically determined based on the system's number of cores. -pref("image.multithreaded_decoding.limit", -1); - // WebGL prefs pref("gl.msaa-level", 2); pref("webgl.force-enabled", false);