LCOV - code coverage report
Current view: top level - image - DecodePool.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 99 146 67.8 %
Date: 2017-07-14 16:53:18 Functions: 18 31 58.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "DecodePool.h"
       7             : 
       8             : #include <algorithm>
       9             : 
      10             : #include "mozilla/ClearOnShutdown.h"
      11             : #include "mozilla/Monitor.h"
      12             : #include "nsCOMPtr.h"
      13             : #include "nsIObserverService.h"
      14             : #include "nsIThreadPool.h"
      15             : #include "nsThreadManager.h"
      16             : #include "nsThreadUtils.h"
      17             : #include "nsXPCOMCIDInternal.h"
      18             : #include "prsystem.h"
      19             : #include "nsIXULRuntime.h"
      20             : 
      21             : #include "gfxPrefs.h"
      22             : 
      23             : #include "Decoder.h"
      24             : #include "IDecodingTask.h"
      25             : #include "RasterImage.h"
      26             : 
      27             : using std::max;
      28             : using std::min;
      29             : 
      30             : namespace mozilla {
      31             : namespace image {
      32             : 
      33             : ///////////////////////////////////////////////////////////////////////////////
      34             : // DecodePool implementation.
      35             : ///////////////////////////////////////////////////////////////////////////////
      36             : 
      37           3 : /* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
      38             : /* static */ uint32_t DecodePool::sNumCores = 0;
      39             : 
      40           6 : NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
      41             : 
      42          60 : struct Work
      43             : {
      44             :   enum class Type {
      45             :     TASK,
      46             :     SHUTDOWN
      47             :   } mType;
      48             : 
      49             :   RefPtr<IDecodingTask> mTask;
      50             : };
      51             : 
      52             : class DecodePoolImpl
      53             : {
      54             : public:
      55             :   MOZ_DECLARE_REFCOUNTED_TYPENAME(DecodePoolImpl)
      56          21 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodePoolImpl)
      57             : 
      58           3 :   DecodePoolImpl()
      59           3 :     : mMonitor("DecodePoolImpl")
      60           3 :     , mShuttingDown(false)
      61           3 :   { }
      62             : 
      63             :   /// Shut down the provided decode pool thread.
      64           0 :   static void ShutdownThread(nsIThread* aThisThread)
      65             :   {
      66             :     // Threads have to be shut down from another thread, so we'll ask the
      67             :     // main thread to do it for us.
      68           0 :     NS_DispatchToMainThread(NewRunnableMethod("DecodePoolImpl::ShutdownThread",
      69           0 :                                               aThisThread, &nsIThread::Shutdown));
      70           0 :   }
      71             : 
      72             :   /**
      73             :    * Requests shutdown. New work items will be dropped on the floor, and all
      74             :    * decode pool threads will be shut down once existing work items have been
      75             :    * processed.
      76             :    */
      77           0 :   void RequestShutdown()
      78             :   {
      79           0 :     MonitorAutoLock lock(mMonitor);
      80           0 :     mShuttingDown = true;
      81           0 :     mMonitor.NotifyAll();
      82           0 :   }
      83             : 
      84             :   /// Pushes a new decode work item.
      85          30 :   void PushWork(IDecodingTask* aTask)
      86             :   {
      87          30 :     MOZ_ASSERT(aTask);
      88          60 :     RefPtr<IDecodingTask> task(aTask);
      89             : 
      90          60 :     MonitorAutoLock lock(mMonitor);
      91             : 
      92          30 :     if (mShuttingDown) {
      93             :       // Drop any new work on the floor if we're shutting down.
      94           0 :       return;
      95             :     }
      96             : 
      97          30 :     if (task->Priority() == TaskPriority::eHigh) {
      98          19 :       mHighPriorityQueue.AppendElement(Move(task));
      99             :     } else {
     100          11 :       mLowPriorityQueue.AppendElement(Move(task));
     101             :     }
     102             : 
     103          30 :     mMonitor.Notify();
     104             :   }
     105             : 
     106             :   /// Pops a new work item, blocking if necessary.
     107          48 :   Work PopWork()
     108             :   {
     109          78 :     MonitorAutoLock lock(mMonitor);
     110             : 
     111             :     do {
     112          78 :       if (!mHighPriorityQueue.IsEmpty()) {
     113          19 :         return PopWorkFromQueue(mHighPriorityQueue);
     114             :       }
     115             : 
     116          59 :       if (!mLowPriorityQueue.IsEmpty()) {
     117          11 :         return PopWorkFromQueue(mLowPriorityQueue);
     118             :       }
     119             : 
     120          48 :       if (mShuttingDown) {
     121           0 :         Work work;
     122           0 :         work.mType = Work::Type::SHUTDOWN;
     123           0 :         return work;
     124             :       }
     125             : 
     126             :       // Nothing to do; block until some work is available.
     127          48 :       mMonitor.Wait();
     128             :     } while (true);
     129             :   }
     130             : 
     131          18 :   nsresult CreateThread(nsIThread** aThread, nsIRunnable* aInitialEvent)
     132             :   {
     133          36 :     return NS_NewNamedThread(mThreadNaming.GetNextThreadName("ImgDecoder"),
     134          36 :                              aThread, aInitialEvent);
     135             :   }
     136             : 
     137             : private:
     138           0 :   ~DecodePoolImpl() { }
     139             : 
     140          30 :   Work PopWorkFromQueue(nsTArray<RefPtr<IDecodingTask>>& aQueue)
     141             :   {
     142          30 :     Work work;
     143          30 :     work.mType = Work::Type::TASK;
     144          30 :     work.mTask = aQueue.LastElement().forget();
     145          30 :     aQueue.RemoveElementAt(aQueue.Length() - 1);
     146             : 
     147          30 :     return work;
     148             :   }
     149             : 
     150             :   nsThreadPoolNaming mThreadNaming;
     151             : 
     152             :   // mMonitor guards the queues and mShuttingDown.
     153             :   Monitor mMonitor;
     154             :   nsTArray<RefPtr<IDecodingTask>> mHighPriorityQueue;
     155             :   nsTArray<RefPtr<IDecodingTask>> mLowPriorityQueue;
     156             :   bool mShuttingDown;
     157             : };
     158             : 
     159           0 : class DecodePoolWorker : public Runnable
     160             : {
     161             : public:
     162          18 :   explicit DecodePoolWorker(DecodePoolImpl* aImpl)
     163          18 :     : Runnable("image::DecodePoolWorker")
     164          18 :     , mImpl(aImpl)
     165          18 :   { }
     166             : 
     167          18 :   NS_IMETHOD Run() override
     168             :   {
     169          18 :     MOZ_ASSERT(!NS_IsMainThread());
     170             : 
     171          18 :     nsCOMPtr<nsIThread> thisThread;
     172          18 :     nsThreadManager::get().GetCurrentThread(getter_AddRefs(thisThread));
     173             : 
     174             :     do {
     175          78 :       Work work = mImpl->PopWork();
     176          30 :       switch (work.mType) {
     177             :         case Work::Type::TASK:
     178          30 :           work.mTask->Run();
     179          30 :           break;
     180             : 
     181             :         case Work::Type::SHUTDOWN:
     182           0 :           DecodePoolImpl::ShutdownThread(thisThread);
     183             : 
     184           0 :           profiler_unregister_thread();
     185             : 
     186           0 :           return NS_OK;
     187             : 
     188             :         default:
     189           0 :           MOZ_ASSERT_UNREACHABLE("Unknown work type");
     190          30 :       }
     191             :     } while (true);
     192             : 
     193             :     MOZ_ASSERT_UNREACHABLE("Exiting thread without Work::Type::SHUTDOWN");
     194             :     return NS_OK;
     195             :   }
     196             : 
     197             : private:
     198             :   RefPtr<DecodePoolImpl> mImpl;
     199             : };
     200             : 
     201             : /* static */ void
     202           3 : DecodePool::Initialize()
     203             : {
     204           3 :   MOZ_ASSERT(NS_IsMainThread());
     205           3 :   sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
     206           3 :   DecodePool::Singleton();
     207           3 : }
     208             : 
     209             : /* static */ DecodePool*
     210          37 : DecodePool::Singleton()
     211             : {
     212          37 :   if (!sSingleton) {
     213           3 :     MOZ_ASSERT(NS_IsMainThread());
     214           3 :     sSingleton = new DecodePool();
     215           3 :     ClearOnShutdown(&sSingleton);
     216             :   }
     217             : 
     218          37 :   return sSingleton;
     219             : }
     220             : 
     221             : /* static */ uint32_t
     222          23 : DecodePool::NumberOfCores()
     223             : {
     224          23 :   return sNumCores;
     225             : }
     226             : 
     227           3 : DecodePool::DecodePool()
     228           3 :   : mImpl(new DecodePoolImpl)
     229           6 :   , mMutex("image::DecodePool")
     230             : {
     231             :   // Determine the number of threads we want.
     232           3 :   int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
     233             :   uint32_t limit;
     234           3 :   if (prefLimit <= 0) {
     235           3 :     int32_t numCores = NumberOfCores();
     236           3 :     if (numCores <= 1) {
     237           0 :       limit = 1;
     238           3 :     } else if (numCores == 2) {
     239             :       // On an otherwise mostly idle system, having two image decoding threads
     240             :       // doubles decoding performance, so it's worth doing on dual-core devices,
     241             :       // even if under load we can't actually get that level of parallelism.
     242           0 :       limit = 2;
     243             :     } else {
     244           3 :       limit = numCores - 1;
     245             :     }
     246             :   } else {
     247           0 :     limit = static_cast<uint32_t>(prefLimit);
     248             :   }
     249           3 :   if (limit > 32) {
     250           0 :     limit = 32;
     251             :   }
     252             :   // The parent process where there are content processes doesn't need as many
     253             :   // threads for decoding images.
     254           3 :   if (limit > 4 && XRE_IsE10sParentProcess()) {
     255           1 :     limit = 4;
     256             :   }
     257             : 
     258             :   // Initialize the thread pool.
     259          21 :   for (uint32_t i = 0 ; i < limit ; ++i) {
     260          54 :     nsCOMPtr<nsIRunnable> worker = new DecodePoolWorker(mImpl);
     261          36 :     nsCOMPtr<nsIThread> thread;
     262          18 :     nsresult rv = mImpl->CreateThread(getter_AddRefs(thread), worker);
     263          18 :     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && thread,
     264             :                        "Should successfully create image decoding threads");
     265          18 :     mThreads.AppendElement(Move(thread));
     266             :   }
     267             : 
     268             :   // Initialize the I/O thread.
     269           3 :   nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
     270           3 :   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
     271             :                      "Should successfully create image I/O thread");
     272             : 
     273           6 :   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     274           3 :   if (obsSvc) {
     275           3 :     obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
     276             :   }
     277           3 : }
     278             : 
     279           0 : DecodePool::~DecodePool()
     280             : {
     281           0 :   MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
     282           0 : }
     283             : 
     284             : NS_IMETHODIMP
     285           0 : DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
     286             : {
     287           0 :   MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
     288             : 
     289           0 :   nsTArray<nsCOMPtr<nsIThread>> threads;
     290           0 :   nsCOMPtr<nsIThread> ioThread;
     291             : 
     292             :   {
     293           0 :     MutexAutoLock lock(mMutex);
     294           0 :     threads.SwapElements(mThreads);
     295           0 :     ioThread.swap(mIOThread);
     296             :   }
     297             : 
     298           0 :   mImpl->RequestShutdown();
     299             : 
     300           0 :   for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
     301           0 :     threads[i]->Shutdown();
     302             :   }
     303             : 
     304           0 :   if (ioThread) {
     305           0 :     ioThread->Shutdown();
     306             :   }
     307             : 
     308           0 :   return NS_OK;
     309             : }
     310             : 
     311             : void
     312          30 : DecodePool::AsyncRun(IDecodingTask* aTask)
     313             : {
     314          30 :   MOZ_ASSERT(aTask);
     315          30 :   mImpl->PushWork(aTask);
     316          30 : }
     317             : 
     318             : bool
     319           3 : DecodePool::SyncRunIfPreferred(IDecodingTask* aTask, const nsCString& aURI)
     320             : {
     321           3 :   MOZ_ASSERT(NS_IsMainThread());
     322           3 :   MOZ_ASSERT(aTask);
     323             : 
     324           6 :   AUTO_PROFILER_LABEL_DYNAMIC("DecodePool::SyncRunIfPreferred", GRAPHICS,
     325             :                               aURI.get());
     326             : 
     327           3 :   if (aTask->ShouldPreferSyncRun()) {
     328           3 :     aTask->Run();
     329           3 :     return true;
     330             :   }
     331             : 
     332           0 :   AsyncRun(aTask);
     333           0 :   return false;
     334             : }
     335             : 
     336             : void
     337           0 : DecodePool::SyncRunIfPossible(IDecodingTask* aTask, const nsCString& aURI)
     338             : {
     339           0 :   MOZ_ASSERT(NS_IsMainThread());
     340           0 :   MOZ_ASSERT(aTask);
     341             : 
     342           0 :   AUTO_PROFILER_LABEL_DYNAMIC("DecodePool::SyncRunIfPossible", GRAPHICS,
     343             :                               aURI.get());
     344             : 
     345           0 :   aTask->Run();
     346           0 : }
     347             : 
     348             : already_AddRefed<nsIEventTarget>
     349           1 : DecodePool::GetIOEventTarget()
     350             : {
     351           2 :   MutexAutoLock threadPoolLock(mMutex);
     352           2 :   nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mIOThread);
     353           2 :   return target.forget();
     354             : }
     355             : 
     356             : } // namespace image
     357             : } // namespace mozilla

Generated by: LCOV version 1.13