Bumping manifests a=b2g-bump
[gecko.git] / image / src / DecodePool.cpp
blobe73ece83ff52cd88ea54e8d44fe0ff76b4a98fad
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/. */
6 #include "DecodePool.h"
8 #include <algorithm>
10 #include "mozilla/ClearOnShutdown.h"
11 #include "nsAutoPtr.h"
12 #include "nsCOMPtr.h"
13 #include "nsIObserverService.h"
14 #include "nsIThreadPool.h"
15 #include "nsThreadUtils.h"
16 #include "nsXPCOMCIDInternal.h"
17 #include "prsystem.h"
19 #ifdef MOZ_NUWA_PROCESS
20 #include "ipc/Nuwa.h"
21 #endif
23 #include "gfxPrefs.h"
25 #include "Decoder.h"
26 #include "RasterImage.h"
28 using std::max;
29 using std::min;
31 namespace mozilla {
32 namespace image {
34 ///////////////////////////////////////////////////////////////////////////////
35 // Helper runnables.
36 ///////////////////////////////////////////////////////////////////////////////
38 class NotifyProgressWorker : public nsRunnable
40 public:
41 /**
42 * Called by the DecodePool when it's done some significant portion of
43 * decoding, so that progress can be recorded and notifications can be sent.
45 static void Dispatch(RasterImage* aImage,
46 Progress aProgress,
47 const nsIntRect& aInvalidRect,
48 uint32_t aFlags)
50 MOZ_ASSERT(aImage);
52 nsCOMPtr<nsIRunnable> worker =
53 new NotifyProgressWorker(aImage, aProgress, aInvalidRect, aFlags);
54 NS_DispatchToMainThread(worker);
57 NS_IMETHOD Run() MOZ_OVERRIDE
59 MOZ_ASSERT(NS_IsMainThread());
60 mImage->NotifyProgress(mProgress, mInvalidRect, mFlags);
61 return NS_OK;
64 private:
65 NotifyProgressWorker(RasterImage* aImage, Progress aProgress,
66 const nsIntRect& aInvalidRect, uint32_t aFlags)
67 : mImage(aImage)
68 , mProgress(aProgress)
69 , mInvalidRect(aInvalidRect)
70 , mFlags(aFlags)
71 { }
73 nsRefPtr<RasterImage> mImage;
74 const Progress mProgress;
75 const nsIntRect mInvalidRect;
76 const uint32_t mFlags;
79 class NotifyDecodeCompleteWorker : public nsRunnable
81 public:
82 /**
83 * Called by the DecodePool when decoding is complete, so that final cleanup
84 * can be performed.
86 static void Dispatch(Decoder* aDecoder)
88 MOZ_ASSERT(aDecoder);
90 nsCOMPtr<nsIRunnable> worker = new NotifyDecodeCompleteWorker(aDecoder);
91 NS_DispatchToMainThread(worker);
94 NS_IMETHOD Run() MOZ_OVERRIDE
96 MOZ_ASSERT(NS_IsMainThread());
97 mDecoder->Finish();
98 mDecoder->GetImage()->FinalizeDecoder(mDecoder);
99 return NS_OK;
102 private:
103 explicit NotifyDecodeCompleteWorker(Decoder* aDecoder)
104 : mDecoder(aDecoder)
107 nsRefPtr<Decoder> mDecoder;
110 class DecodeWorker : public nsRunnable
112 public:
113 explicit DecodeWorker(Decoder* aDecoder)
114 : mDecoder(aDecoder)
116 MOZ_ASSERT(mDecoder);
119 NS_IMETHOD Run() MOZ_OVERRIDE
121 MOZ_ASSERT(!NS_IsMainThread());
122 DecodePool::Singleton()->Decode(mDecoder);
123 return NS_OK;
126 private:
127 nsRefPtr<Decoder> mDecoder;
130 #ifdef MOZ_NUWA_PROCESS
132 class DecodePoolNuwaListener MOZ_FINAL : public nsIThreadPoolListener
134 public:
135 NS_DECL_THREADSAFE_ISUPPORTS
137 NS_IMETHODIMP OnThreadCreated()
139 if (IsNuwaProcess()) {
140 NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
142 return NS_OK;
145 NS_IMETHODIMP OnThreadShuttingDown() { return NS_OK; }
147 private:
148 ~DecodePoolNuwaListener() { }
151 NS_IMPL_ISUPPORTS(DecodePoolNuwaListener, nsIThreadPoolListener)
153 class RegisterDecodeIOThreadWithNuwaRunnable : public nsRunnable
155 public:
156 NS_IMETHOD Run()
158 NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
159 return NS_OK;
162 #endif // MOZ_NUWA_PROCESS
165 ///////////////////////////////////////////////////////////////////////////////
166 // DecodePool implementation.
167 ///////////////////////////////////////////////////////////////////////////////
169 /* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
171 NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
173 /* static */ void
174 DecodePool::Initialize()
176 MOZ_ASSERT(NS_IsMainThread());
177 DecodePool::Singleton();
180 /* static */ DecodePool*
181 DecodePool::Singleton()
183 if (!sSingleton) {
184 MOZ_ASSERT(NS_IsMainThread());
185 sSingleton = new DecodePool();
186 ClearOnShutdown(&sSingleton);
189 return sSingleton;
192 DecodePool::DecodePool()
193 : mMutex("image::DecodePool")
195 // Initialize the thread pool.
196 mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
197 MOZ_RELEASE_ASSERT(mThreadPool,
198 "Should succeed in creating image decoding thread pool");
200 mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
201 int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
202 uint32_t limit;
203 if (prefLimit <= 0) {
204 limit = max(PR_GetNumberOfProcessors(), 2) - 1;
205 } else {
206 limit = static_cast<uint32_t>(prefLimit);
209 mThreadPool->SetThreadLimit(limit);
210 mThreadPool->SetIdleThreadLimit(limit);
212 #ifdef MOZ_NUWA_PROCESS
213 if (IsNuwaProcess()) {
214 mThreadPool->SetListener(new DecodePoolNuwaListener());
216 #endif
218 // Initialize the I/O thread.
219 nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
220 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
221 "Should successfully create image I/O thread");
223 #ifdef MOZ_NUWA_PROCESS
224 nsCOMPtr<nsIRunnable> worker = new RegisterDecodeIOThreadWithNuwaRunnable();
225 rv = mIOThread->Dispatch(worker, NS_DISPATCH_NORMAL);
226 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
227 "Should register decode IO thread with Nuwa process");
228 #endif
230 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
231 if (obsSvc) {
232 obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
236 DecodePool::~DecodePool()
238 MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
241 NS_IMETHODIMP
242 DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
244 MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
246 nsCOMPtr<nsIThreadPool> threadPool;
247 nsCOMPtr<nsIThread> ioThread;
250 MutexAutoLock lock(mMutex);
251 threadPool.swap(mThreadPool);
252 ioThread.swap(mIOThread);
255 if (threadPool) {
256 threadPool->Shutdown();
259 if (ioThread) {
260 ioThread->Shutdown();
263 return NS_OK;
266 void
267 DecodePool::AsyncDecode(Decoder* aDecoder)
269 MOZ_ASSERT(aDecoder);
271 nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
273 // Dispatch to the thread pool if it exists. If it doesn't, we're currently
274 // shutting down, so it's OK to just drop the job on the floor.
275 MutexAutoLock threadPoolLock(mMutex);
276 if (mThreadPool) {
277 mThreadPool->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
281 void
282 DecodePool::SyncDecodeIfSmall(Decoder* aDecoder)
284 MOZ_ASSERT(NS_IsMainThread());
285 MOZ_ASSERT(aDecoder);
287 if (aDecoder->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime())) {
288 Decode(aDecoder);
289 return;
292 AsyncDecode(aDecoder);
295 void
296 DecodePool::SyncDecodeIfPossible(Decoder* aDecoder)
298 MOZ_ASSERT(NS_IsMainThread());
299 Decode(aDecoder);
302 already_AddRefed<nsIEventTarget>
303 DecodePool::GetEventTarget()
305 MutexAutoLock threadPoolLock(mMutex);
306 nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
307 return target.forget();
310 already_AddRefed<nsIEventTarget>
311 DecodePool::GetIOEventTarget()
313 MutexAutoLock threadPoolLock(mMutex);
314 nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mIOThread);
315 return target.forget();
318 already_AddRefed<nsIRunnable>
319 DecodePool::CreateDecodeWorker(Decoder* aDecoder)
321 MOZ_ASSERT(aDecoder);
322 nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
323 return worker.forget();
326 void
327 DecodePool::Decode(Decoder* aDecoder)
329 MOZ_ASSERT(aDecoder);
331 nsresult rv = aDecoder->Decode();
333 if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
334 if (aDecoder->HasProgress()) {
335 NotifyProgress(aDecoder);
337 // The decoder will ensure that a new worker gets enqueued to continue
338 // decoding when more data is available.
339 } else {
340 NotifyDecodeComplete(aDecoder);
344 void
345 DecodePool::NotifyProgress(Decoder* aDecoder)
347 MOZ_ASSERT(aDecoder);
349 if (!NS_IsMainThread() ||
350 (aDecoder->GetFlags() & imgIContainer::FLAG_ASYNC_NOTIFY)) {
351 NotifyProgressWorker::Dispatch(aDecoder->GetImage(),
352 aDecoder->TakeProgress(),
353 aDecoder->TakeInvalidRect(),
354 aDecoder->GetDecodeFlags());
355 return;
358 aDecoder->GetImage()->NotifyProgress(aDecoder->TakeProgress(),
359 aDecoder->TakeInvalidRect(),
360 aDecoder->GetDecodeFlags());
363 void
364 DecodePool::NotifyDecodeComplete(Decoder* aDecoder)
366 MOZ_ASSERT(aDecoder);
368 if (!NS_IsMainThread() ||
369 (aDecoder->GetFlags() & imgIContainer::FLAG_ASYNC_NOTIFY)) {
370 NotifyDecodeCompleteWorker::Dispatch(aDecoder);
371 return;
374 aDecoder->Finish();
375 aDecoder->GetImage()->FinalizeDecoder(aDecoder);
378 } // namespace image
379 } // namespace mozilla