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
|