DecodePool.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. #include "DecodePool.h"
  6. #include <algorithm>
  7. #include "mozilla/ClearOnShutdown.h"
  8. #include "mozilla/Monitor.h"
  9. #include "nsCOMPtr.h"
  10. #include "nsIObserverService.h"
  11. #include "nsIThreadPool.h"
  12. #include "nsThreadManager.h"
  13. #include "nsThreadUtils.h"
  14. #include "nsXPCOMCIDInternal.h"
  15. #include "prsystem.h"
  16. #include "gfxPrefs.h"
  17. #include "Decoder.h"
  18. #include "IDecodingTask.h"
  19. #include "RasterImage.h"
  20. using std::max;
  21. using std::min;
  22. namespace mozilla {
  23. namespace image {
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // DecodePool implementation.
  26. ///////////////////////////////////////////////////////////////////////////////
  27. /* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
  28. /* static */ uint32_t DecodePool::sNumCores = 0;
  29. NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
  30. struct Work
  31. {
  32. enum class Type {
  33. TASK,
  34. SHUTDOWN
  35. } mType;
  36. RefPtr<IDecodingTask> mTask;
  37. };
  38. class DecodePoolImpl
  39. {
  40. public:
  41. MOZ_DECLARE_REFCOUNTED_TYPENAME(DecodePoolImpl)
  42. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodePoolImpl)
  43. DecodePoolImpl()
  44. : mMonitor("DecodePoolImpl")
  45. , mShuttingDown(false)
  46. { }
  47. /// Initialize the current thread for use by the decode pool.
  48. void InitCurrentThread()
  49. {
  50. MOZ_ASSERT(!NS_IsMainThread());
  51. mThreadNaming.SetThreadPoolName(NS_LITERAL_CSTRING("ImgDecoder"));
  52. }
  53. /// Shut down the provided decode pool thread.
  54. static void ShutdownThread(nsIThread* aThisThread)
  55. {
  56. // Threads have to be shut down from another thread, so we'll ask the
  57. // main thread to do it for us.
  58. NS_DispatchToMainThread(NewRunnableMethod(aThisThread, &nsIThread::Shutdown));
  59. }
  60. /**
  61. * Requests shutdown. New work items will be dropped on the floor, and all
  62. * decode pool threads will be shut down once existing work items have been
  63. * processed.
  64. */
  65. void RequestShutdown()
  66. {
  67. MonitorAutoLock lock(mMonitor);
  68. mShuttingDown = true;
  69. mMonitor.NotifyAll();
  70. }
  71. /// Pushes a new decode work item.
  72. void PushWork(IDecodingTask* aTask)
  73. {
  74. MOZ_ASSERT(aTask);
  75. RefPtr<IDecodingTask> task(aTask);
  76. MonitorAutoLock lock(mMonitor);
  77. if (mShuttingDown) {
  78. // Drop any new work on the floor if we're shutting down.
  79. return;
  80. }
  81. if (task->Priority() == TaskPriority::eHigh) {
  82. mHighPriorityQueue.AppendElement(Move(task));
  83. } else {
  84. mLowPriorityQueue.AppendElement(Move(task));
  85. }
  86. mMonitor.Notify();
  87. }
  88. /// Pops a new work item, blocking if necessary.
  89. Work PopWork()
  90. {
  91. MonitorAutoLock lock(mMonitor);
  92. do {
  93. if (!mHighPriorityQueue.IsEmpty()) {
  94. return PopWorkFromQueue(mHighPriorityQueue);
  95. }
  96. if (!mLowPriorityQueue.IsEmpty()) {
  97. return PopWorkFromQueue(mLowPriorityQueue);
  98. }
  99. if (mShuttingDown) {
  100. Work work;
  101. work.mType = Work::Type::SHUTDOWN;
  102. return work;
  103. }
  104. // Nothing to do; block until some work is available.
  105. mMonitor.Wait();
  106. } while (true);
  107. }
  108. private:
  109. ~DecodePoolImpl() { }
  110. Work PopWorkFromQueue(nsTArray<RefPtr<IDecodingTask>>& aQueue)
  111. {
  112. Work work;
  113. work.mType = Work::Type::TASK;
  114. work.mTask = aQueue.LastElement().forget();
  115. aQueue.RemoveElementAt(aQueue.Length() - 1);
  116. return work;
  117. }
  118. nsThreadPoolNaming mThreadNaming;
  119. // mMonitor guards the queues and mShuttingDown.
  120. Monitor mMonitor;
  121. nsTArray<RefPtr<IDecodingTask>> mHighPriorityQueue;
  122. nsTArray<RefPtr<IDecodingTask>> mLowPriorityQueue;
  123. bool mShuttingDown;
  124. };
  125. class DecodePoolWorker : public Runnable
  126. {
  127. public:
  128. explicit DecodePoolWorker(DecodePoolImpl* aImpl) : mImpl(aImpl) { }
  129. NS_IMETHOD Run() override
  130. {
  131. MOZ_ASSERT(!NS_IsMainThread());
  132. mImpl->InitCurrentThread();
  133. nsCOMPtr<nsIThread> thisThread;
  134. nsThreadManager::get().GetCurrentThread(getter_AddRefs(thisThread));
  135. do {
  136. Work work = mImpl->PopWork();
  137. switch (work.mType) {
  138. case Work::Type::TASK:
  139. work.mTask->Run();
  140. break;
  141. case Work::Type::SHUTDOWN:
  142. DecodePoolImpl::ShutdownThread(thisThread);
  143. return NS_OK;
  144. default:
  145. MOZ_ASSERT_UNREACHABLE("Unknown work type");
  146. }
  147. } while (true);
  148. MOZ_ASSERT_UNREACHABLE("Exiting thread without Work::Type::SHUTDOWN");
  149. return NS_OK;
  150. }
  151. private:
  152. RefPtr<DecodePoolImpl> mImpl;
  153. };
  154. /* static */ void
  155. DecodePool::Initialize()
  156. {
  157. MOZ_ASSERT(NS_IsMainThread());
  158. sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
  159. DecodePool::Singleton();
  160. }
  161. /* static */ DecodePool*
  162. DecodePool::Singleton()
  163. {
  164. if (!sSingleton) {
  165. MOZ_ASSERT(NS_IsMainThread());
  166. sSingleton = new DecodePool();
  167. ClearOnShutdown(&sSingleton);
  168. }
  169. return sSingleton;
  170. }
  171. /* static */ uint32_t
  172. DecodePool::NumberOfCores()
  173. {
  174. return sNumCores;
  175. }
  176. DecodePool::DecodePool()
  177. : mImpl(new DecodePoolImpl)
  178. , mMutex("image::DecodePool")
  179. {
  180. // Determine the number of threads we want.
  181. int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
  182. uint32_t limit;
  183. if (prefLimit <= 0) {
  184. int32_t numCores = NumberOfCores();
  185. if (numCores <= 1) {
  186. limit = 1;
  187. } else if (numCores == 2) {
  188. // On an otherwise mostly idle system, having two image decoding threads
  189. // doubles decoding performance, so it's worth doing on dual-core devices,
  190. // even if under load we can't actually get that level of parallelism.
  191. limit = 2;
  192. } else {
  193. limit = numCores - 1;
  194. }
  195. } else {
  196. limit = static_cast<uint32_t>(prefLimit);
  197. }
  198. if (limit > 32) {
  199. limit = 32;
  200. }
  201. // Initialize the thread pool.
  202. for (uint32_t i = 0 ; i < limit ; ++i) {
  203. nsCOMPtr<nsIRunnable> worker = new DecodePoolWorker(mImpl);
  204. nsCOMPtr<nsIThread> thread;
  205. nsresult rv = NS_NewThread(getter_AddRefs(thread), worker);
  206. MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && thread,
  207. "Should successfully create image decoding threads");
  208. mThreads.AppendElement(Move(thread));
  209. }
  210. // Initialize the I/O thread.
  211. nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
  212. MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
  213. "Should successfully create image I/O thread");
  214. nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
  215. if (obsSvc) {
  216. obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
  217. }
  218. }
  219. DecodePool::~DecodePool()
  220. {
  221. MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
  222. }
  223. NS_IMETHODIMP
  224. DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
  225. {
  226. MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
  227. nsTArray<nsCOMPtr<nsIThread>> threads;
  228. nsCOMPtr<nsIThread> ioThread;
  229. {
  230. MutexAutoLock lock(mMutex);
  231. threads.SwapElements(mThreads);
  232. ioThread.swap(mIOThread);
  233. }
  234. mImpl->RequestShutdown();
  235. for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
  236. threads[i]->Shutdown();
  237. }
  238. if (ioThread) {
  239. ioThread->Shutdown();
  240. }
  241. return NS_OK;
  242. }
  243. void
  244. DecodePool::AsyncRun(IDecodingTask* aTask)
  245. {
  246. MOZ_ASSERT(aTask);
  247. mImpl->PushWork(aTask);
  248. }
  249. void
  250. DecodePool::SyncRunIfPreferred(IDecodingTask* aTask)
  251. {
  252. MOZ_ASSERT(NS_IsMainThread());
  253. MOZ_ASSERT(aTask);
  254. if (aTask->ShouldPreferSyncRun()) {
  255. aTask->Run();
  256. return;
  257. }
  258. AsyncRun(aTask);
  259. }
  260. void
  261. DecodePool::SyncRunIfPossible(IDecodingTask* aTask)
  262. {
  263. MOZ_ASSERT(NS_IsMainThread());
  264. MOZ_ASSERT(aTask);
  265. aTask->Run();
  266. }
  267. already_AddRefed<nsIEventTarget>
  268. DecodePool::GetIOEventTarget()
  269. {
  270. MutexAutoLock threadPoolLock(mMutex);
  271. nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mIOThread);
  272. return target.forget();
  273. }
  274. } // namespace image
  275. } // namespace mozilla