DecodePool.cpp (5637B)
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 "mozilla/ProfilerLabels.h" 13 #include "mozilla/SchedulerGroup.h" 14 #include "mozilla/Services.h" 15 #include "mozilla/StaticPrefs_image.h" 16 #include "mozilla/TaskController.h" 17 #include "mozilla/TimeStamp.h" 18 #include "mozilla/AppShutdown.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 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 */ 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 */ 71 uint32_t DecodePool::NumberOfCores() { return sNumCores; } 72 73 #if defined(XP_WIN) 74 class IOThreadIniter final : public Runnable { 75 public: 76 explicit IOThreadIniter() : Runnable("image::IOThreadIniter") {} 77 78 NS_IMETHOD Run() override { 79 MOZ_ASSERT(!NS_IsMainThread()); 80 81 CoInitialize(nullptr); 82 83 return NS_OK; 84 } 85 }; 86 #endif 87 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 111 DecodePool::~DecodePool() { 112 MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!"); 113 } 114 115 NS_IMETHODIMP 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 135 /* static */ bool DecodePool::IsShuttingDown() { 136 if (MOZ_UNLIKELY(!sSingleton)) { 137 return AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads); 138 } 139 140 return sSingleton->mShuttingDown; 141 } 142 143 class DecodingTask final : public Task { 144 public: 145 explicit DecodingTask(RefPtr<IDecodingTask>&& aTask) 146 : Task(Kind::OffMainThreadOnly, aTask->Priority() == TaskPriority::eLow 147 ? EventQueuePriority::Normal 148 : EventQueuePriority::RenderBlocking), 149 mTask(aTask) {} 150 151 TaskResult Run() override { 152 mTask->Run(); 153 return TaskResult::Complete; 154 } 155 156 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY 157 bool GetName(nsACString& aName) override { 158 aName.AssignLiteral("ImageDecodingTask"); 159 return true; 160 } 161 #endif 162 163 private: 164 RefPtr<IDecodingTask> mTask; 165 }; 166 167 void DecodePool::AsyncRun(IDecodingTask* aTask) { 168 MOZ_ASSERT(aTask); 169 170 TaskController::Get()->AddTask( 171 MakeAndAddRef<DecodingTask>((RefPtr<IDecodingTask>(aTask)))); 172 } 173 174 bool DecodePool::SyncRunIfPreferred(IDecodingTask* aTask, 175 const nsCString& aURI) { 176 MOZ_ASSERT(NS_IsMainThread()); 177 MOZ_ASSERT(aTask); 178 179 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("DecodePool::SyncRunIfPreferred", 180 GRAPHICS, aURI); 181 182 if (aTask->ShouldPreferSyncRun()) { 183 aTask->Run(); 184 return true; 185 } 186 187 AsyncRun(aTask); 188 return false; 189 } 190 191 void DecodePool::SyncRunIfPossible(IDecodingTask* aTask, 192 const nsCString& aURI) { 193 MOZ_ASSERT(NS_IsMainThread()); 194 MOZ_ASSERT(aTask); 195 196 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("DecodePool::SyncRunIfPossible", 197 GRAPHICS, aURI); 198 199 aTask->Run(); 200 } 201 202 already_AddRefed<nsISerialEventTarget> DecodePool::GetIOEventTarget() { 203 MutexAutoLock threadPoolLock(mMutex); 204 nsCOMPtr<nsISerialEventTarget> target = mIOThread; 205 return target.forget(); 206 } 207 208 } // namespace image 209 } // namespace mozilla