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"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "nsAutoPtr.h"
13 #include "nsIObserverService.h"
14 #include "nsIThreadPool.h"
15 #include "nsThreadUtils.h"
16 #include "nsXPCOMCIDInternal.h"
19 #ifdef MOZ_NUWA_PROCESS
26 #include "RasterImage.h"
34 ///////////////////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////////////////
38 class NotifyProgressWorker
: public nsRunnable
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
,
47 const nsIntRect
& aInvalidRect
,
52 nsCOMPtr
<nsIRunnable
> worker
=
53 new NotifyProgressWorker(aImage
, aProgress
, aInvalidRect
, aFlags
);
54 NS_DispatchToMainThread(worker
);
57 NS_IMETHOD
Run() override
59 MOZ_ASSERT(NS_IsMainThread());
60 mImage
->NotifyProgress(mProgress
, mInvalidRect
, mFlags
);
65 NotifyProgressWorker(RasterImage
* aImage
, Progress aProgress
,
66 const nsIntRect
& aInvalidRect
, uint32_t aFlags
)
68 , mProgress(aProgress
)
69 , mInvalidRect(aInvalidRect
)
73 nsRefPtr
<RasterImage
> mImage
;
74 const Progress mProgress
;
75 const nsIntRect mInvalidRect
;
76 const uint32_t mFlags
;
79 class NotifyDecodeCompleteWorker
: public nsRunnable
83 * Called by the DecodePool when decoding is complete, so that final cleanup
86 static void Dispatch(Decoder
* aDecoder
)
90 nsCOMPtr
<nsIRunnable
> worker
= new NotifyDecodeCompleteWorker(aDecoder
);
91 NS_DispatchToMainThread(worker
);
94 NS_IMETHOD
Run() override
96 MOZ_ASSERT(NS_IsMainThread());
98 mDecoder
->GetImage()->FinalizeDecoder(mDecoder
);
103 explicit NotifyDecodeCompleteWorker(Decoder
* aDecoder
)
107 nsRefPtr
<Decoder
> mDecoder
;
110 class DecodeWorker
: public nsRunnable
113 explicit DecodeWorker(Decoder
* aDecoder
)
116 MOZ_ASSERT(mDecoder
);
119 NS_IMETHOD
Run() override
121 MOZ_ASSERT(!NS_IsMainThread());
122 DecodePool::Singleton()->Decode(mDecoder
);
127 nsRefPtr
<Decoder
> mDecoder
;
130 #ifdef MOZ_NUWA_PROCESS
132 class DecodePoolNuwaListener final
: public nsIThreadPoolListener
135 NS_DECL_THREADSAFE_ISUPPORTS
137 NS_IMETHODIMP
OnThreadCreated()
139 if (IsNuwaProcess()) {
140 NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
145 NS_IMETHODIMP
OnThreadShuttingDown() { return NS_OK
; }
148 ~DecodePoolNuwaListener() { }
151 NS_IMPL_ISUPPORTS(DecodePoolNuwaListener
, nsIThreadPoolListener
)
153 class RegisterDecodeIOThreadWithNuwaRunnable
: public nsRunnable
158 NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
162 #endif // MOZ_NUWA_PROCESS
165 ///////////////////////////////////////////////////////////////////////////////
166 // DecodePool implementation.
167 ///////////////////////////////////////////////////////////////////////////////
169 /* static */ StaticRefPtr
<DecodePool
> DecodePool::sSingleton
;
171 NS_IMPL_ISUPPORTS(DecodePool
, nsIObserver
)
174 DecodePool::Initialize()
176 MOZ_ASSERT(NS_IsMainThread());
177 DecodePool::Singleton();
180 /* static */ DecodePool
*
181 DecodePool::Singleton()
184 MOZ_ASSERT(NS_IsMainThread());
185 sSingleton
= new DecodePool();
186 ClearOnShutdown(&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();
203 if (prefLimit
<= 0) {
204 limit
= max(PR_GetNumberOfProcessors(), 2) - 1;
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());
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");
230 nsCOMPtr
<nsIObserverService
> obsSvc
= services::GetObserverService();
232 obsSvc
->AddObserver(this, "xpcom-shutdown-threads", false);
236 DecodePool::~DecodePool()
238 MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
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
);
256 threadPool
->Shutdown();
260 ioThread
->Shutdown();
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
);
277 mThreadPool
->Dispatch(worker
, nsIEventTarget::DISPATCH_NORMAL
);
282 DecodePool::SyncDecodeIfSmall(Decoder
* aDecoder
)
284 MOZ_ASSERT(NS_IsMainThread());
285 MOZ_ASSERT(aDecoder
);
287 if (aDecoder
->ShouldSyncDecode(gfxPrefs::ImageMemDecodeBytesAtATime())) {
292 AsyncDecode(aDecoder
);
296 DecodePool::SyncDecodeIfPossible(Decoder
* aDecoder
)
298 MOZ_ASSERT(NS_IsMainThread());
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();
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.
340 NotifyDecodeComplete(aDecoder
);
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());
358 aDecoder
->GetImage()->NotifyProgress(aDecoder
->TakeProgress(),
359 aDecoder
->TakeInvalidRect(),
360 aDecoder
->GetDecodeFlags());
364 DecodePool::NotifyDecodeComplete(Decoder
* aDecoder
)
366 MOZ_ASSERT(aDecoder
);
368 if (!NS_IsMainThread() ||
369 (aDecoder
->GetFlags() & imgIContainer::FLAG_ASYNC_NOTIFY
)) {
370 NotifyDecodeCompleteWorker::Dispatch(aDecoder
);
375 aDecoder
->GetImage()->FinalizeDecoder(aDecoder
);
379 } // namespace mozilla