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/DebugOnly.h"
12 #include "mozilla/Monitor.h"
13 #include "mozilla/ProfilerLabels.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/StaticPrefs_image.h"
17 #include "mozilla/TaskController.h"
18 #include "mozilla/TimeStamp.h"
19 #include "nsCOMPtr.h"
20 #include "nsIObserverService.h"
21 #include "nsThreadManager.h"
22 #include "nsThreadUtils.h"
23 #include "nsXPCOMCIDInternal.h"
24 #include "prsystem.h"
25 
26 #include "Decoder.h"
27 #include "IDecodingTask.h"
28 #include "RasterImage.h"
29 
30 #if defined(XP_WIN)
31 #  include <objbase.h>
32 #  include "mozilla/WindowsProcessMitigations.h"
33 #endif
34 
35 using std::max;
36 using std::min;
37 
38 namespace mozilla {
39 namespace image {
40 
41 ///////////////////////////////////////////////////////////////////////////////
42 // DecodePool implementation.
43 ///////////////////////////////////////////////////////////////////////////////
44 
45 /* static */
46 StaticRefPtr<DecodePool> DecodePool::sSingleton;
47 /* static */
48 uint32_t DecodePool::sNumCores = 0;
49 
NS_IMPL_ISUPPORTS(DecodePool,nsIObserver)50 NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
51 
52 /* static */
53 void DecodePool::Initialize() {
54   MOZ_ASSERT(NS_IsMainThread());
55   sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
56   DecodePool::Singleton();
57 }
58 
59 /* static */
Singleton()60 DecodePool* DecodePool::Singleton() {
61   if (!sSingleton) {
62     MOZ_ASSERT(NS_IsMainThread());
63     sSingleton = new DecodePool();
64     ClearOnShutdown(&sSingleton);
65   }
66 
67   return sSingleton;
68 }
69 
70 /* static */
NumberOfCores()71 uint32_t DecodePool::NumberOfCores() { return sNumCores; }
72 
73 #if defined(XP_WIN)
74 class IOThreadIniter final : public Runnable {
75  public:
IOThreadIniter()76   explicit IOThreadIniter() : Runnable("image::IOThreadIniter") {}
77 
Run()78   NS_IMETHOD Run() override {
79     MOZ_ASSERT(!NS_IsMainThread());
80 
81     CoInitialize(nullptr);
82 
83     return NS_OK;
84   }
85 };
86 #endif
87 
DecodePool()88 DecodePool::DecodePool() : mMutex("image::IOThread") {
89   // Initialize the I/O thread.
90 #if defined(XP_WIN)
91   // On Windows we use the io thread to get icons from the system. Any thread
92   // that makes system calls needs to call CoInitialize. And these system calls
93   // (SHGetFileInfo) should only be called from one thread at a time, in case
94   // we ever create more than one io thread. If win32k is locked down, we can't
95   // call SHGetFileInfo anyway, so we don't need the initializer.
96   nsCOMPtr<nsIRunnable> initer =
97       IsWin32kLockedDown() ? nullptr : new IOThreadIniter();
98   nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread), initer);
99 #else
100   nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
101 #endif
102   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
103                      "Should successfully create image I/O thread");
104 
105   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
106   if (obsSvc) {
107     obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
108   }
109 }
110 
~DecodePool()111 DecodePool::~DecodePool() {
112   MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
113 }
114 
115 NS_IMETHODIMP
Observe(nsISupports *,const char * aTopic,const char16_t *)116 DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*) {
117   MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
118 
119   mShuttingDown = true;
120 
121   nsCOMPtr<nsIThread> ioThread;
122 
123   {
124     MutexAutoLock lock(mMutex);
125     ioThread.swap(mIOThread);
126   }
127 
128   if (ioThread) {
129     ioThread->Shutdown();
130   }
131 
132   return NS_OK;
133 }
134 
IsShuttingDown() const135 bool DecodePool::IsShuttingDown() const { return mShuttingDown; }
136 
137 class DecodingTask final : public Task {
138  public:
DecodingTask(RefPtr<IDecodingTask> && aTask)139   explicit DecodingTask(RefPtr<IDecodingTask>&& aTask)
140       : Task(false, aTask->Priority() == TaskPriority::eLow
141                         ? EventQueuePriority::Normal
142                         : EventQueuePriority::MediumHigh),
143         mTask(aTask) {}
144 
Run()145   bool Run() override {
146     mTask->Run();
147     return true;
148   }
149 
150  private:
151   RefPtr<IDecodingTask> mTask;
152 };
153 
AsyncRun(IDecodingTask * aTask)154 void DecodePool::AsyncRun(IDecodingTask* aTask) {
155   MOZ_ASSERT(aTask);
156 
157   TaskController::Get()->AddTask(
158       MakeAndAddRef<DecodingTask>((RefPtr<IDecodingTask>(aTask))));
159 }
160 
SyncRunIfPreferred(IDecodingTask * aTask,const nsCString & aURI)161 bool DecodePool::SyncRunIfPreferred(IDecodingTask* aTask,
162                                     const nsCString& aURI) {
163   MOZ_ASSERT(NS_IsMainThread());
164   MOZ_ASSERT(aTask);
165 
166   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("DecodePool::SyncRunIfPreferred",
167                                         GRAPHICS, aURI);
168 
169   if (aTask->ShouldPreferSyncRun()) {
170     aTask->Run();
171     return true;
172   }
173 
174   AsyncRun(aTask);
175   return false;
176 }
177 
SyncRunIfPossible(IDecodingTask * aTask,const nsCString & aURI)178 void DecodePool::SyncRunIfPossible(IDecodingTask* aTask,
179                                    const nsCString& aURI) {
180   MOZ_ASSERT(NS_IsMainThread());
181   MOZ_ASSERT(aTask);
182 
183   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("DecodePool::SyncRunIfPossible",
184                                         GRAPHICS, aURI);
185 
186   aTask->Run();
187 }
188 
GetIOEventTarget()189 already_AddRefed<nsIEventTarget> DecodePool::GetIOEventTarget() {
190   MutexAutoLock threadPoolLock(mMutex);
191   nsCOMPtr<nsIEventTarget> target = mIOThread;
192   return target.forget();
193 }
194 
195 }  // namespace image
196 }  // namespace mozilla
197