Bug 857367 - Make it safe to call RasterImage::DecodePool::RequestDecode off the main thread. r=tn
authorSeth Fowler <seth@mozilla.com>
Thu, 04 Apr 2013 18:01:41 -0700
changeset 134513 503dea706f82dcc671e73aae1b2ce8226bec8bb9
parent 134512 e63cb4c3e06320521b32cf08465f66fa728d35f3
child 134514 2edc2af0ec32bdaad1900b2459f928536920c257
push idunknown
push userunknown
push dateunknown
reviewerstn
bugs857367
milestone23.0a1
Bug 857367 - Make it safe to call RasterImage::DecodePool::RequestDecode off the main thread. r=tn
image/src/RasterImage.cpp
image/src/RasterImage.h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -3592,16 +3592,18 @@ RasterImage::DecodePool::Singleton()
     sSingleton = new DecodePool();
     ClearOnShutdown(&sSingleton);
   }
 
   return sSingleton;
 }
 
 RasterImage::DecodePool::DecodePool()
+  : mThreadPoolMutex("Thread Pool")
+  , mShuttingDown(false)
 {
   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 {
@@ -3618,33 +3620,40 @@ RasterImage::DecodePool::DecodePool()
 
 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)
+                                 const PRUnichar *data)
 {
   NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
 
-  if (mThreadPool) {
-    mThreadPool->Shutdown();
+  nsCOMPtr<nsIThreadPool> threadPool;
+
+  {
+    MutexAutoLock threadPoolLock(mThreadPoolMutex);
+    threadPool = mThreadPool;
     mThreadPool = nullptr;
+    mShuttingDown = true;
+  }
+
+  if (threadPool) {
+    threadPool->Shutdown();
   }
 
   return NS_OK;
 }
 
 void
 RasterImage::DecodePool::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;
@@ -3653,17 +3662,20 @@ RasterImage::DecodePool::RequestDecode(R
         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<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg);
-    if (!gMultithreadedDecoding || !mThreadPool) {
+    MutexAutoLock threadPoolLock(mThreadPoolMutex);
+    if (mShuttingDown) {
+      // Just drop the job on the floor; we won't need it.
+    } else if (!gMultithreadedDecoding || !mThreadPool) {
       NS_DispatchToMainThread(job);
     } else {
       mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
     }
   }
 }
 
 void
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -520,17 +520,22 @@ private:
 
     private:
       nsRefPtr<DecodeRequest> mRequest;
       nsRefPtr<RasterImage> mImage;
     };
 
   private: /* members */
 
+    // mThreadPoolMutex protects both mThreadPool and mShuttingDown. For all
+    // RasterImages R, R::mDecodingMutex must be acquired before
+    // mThreadPoolMutex if both are acquired; the other order may cause deadlock.
+    mozilla::Mutex          mThreadPoolMutex;
     nsCOMPtr<nsIThreadPool> mThreadPool;
+    bool                    mShuttingDown;
   };
 
   class DecodeDoneWorker : public nsRunnable
   {
   public:
     /**
      * Called by the DecodePool with an image when it's done some significant
      * portion of decoding that needs to be notified about.