gecko-dev/image/DecodePool.cpp
Wes Kocher 26a1ba5c14 Backed out 27 changesets (bug 1323100) for clipboard leaktest failures a=backout
Backed out changeset 84fb749698ab (bug 1323100)
Backed out changeset d6d25e8bd001 (bug 1323100)
Backed out changeset 1b0855bb0c38 (bug 1323100)
Backed out changeset b6953e3f5739 (bug 1323100)
Backed out changeset 5572f3b63215 (bug 1323100)
Backed out changeset 12fb4c533659 (bug 1323100)
Backed out changeset c36524e4e919 (bug 1323100)
Backed out changeset 1e3b3eddbe26 (bug 1323100)
Backed out changeset 061110f1ae12 (bug 1323100)
Backed out changeset 413dbd31725b (bug 1323100)
Backed out changeset 06550f7eca62 (bug 1323100)
Backed out changeset 940933b13b36 (bug 1323100)
Backed out changeset a6d75c1cd724 (bug 1323100)
Backed out changeset 681cacbbaa3b (bug 1323100)
Backed out changeset 3d53787293f6 (bug 1323100)
Backed out changeset c0340dfe4766 (bug 1323100)
Backed out changeset 9f554991549d (bug 1323100)
Backed out changeset 757539e7039a (bug 1323100)
Backed out changeset a3c9b45aa917 (bug 1323100)
Backed out changeset 23d69df98a66 (bug 1323100)
Backed out changeset 1297ded6a01d (bug 1323100)
Backed out changeset f4235b97257f (bug 1323100)
Backed out changeset 93419cb4f29f (bug 1323100)
Backed out changeset 865d1b81c804 (bug 1323100)
Backed out changeset 54acf4ef8e84 (bug 1323100)
Backed out changeset 88d17bcd8205 (bug 1323100)
Backed out changeset 0c466e5e8933 (bug 1323100)
2016-12-29 16:28:36 -08:00

351 lines
8.1 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DecodePool.h"
#include <algorithm>
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Monitor.h"
#include "nsCOMPtr.h"
#include "nsIObserverService.h"
#include "nsIThreadPool.h"
#include "nsThreadManager.h"
#include "nsThreadUtils.h"
#include "nsXPCOMCIDInternal.h"
#include "prsystem.h"
#include "nsIXULRuntime.h"
#include "gfxPrefs.h"
#include "Decoder.h"
#include "IDecodingTask.h"
#include "RasterImage.h"
using std::max;
using std::min;
namespace mozilla {
namespace image {
///////////////////////////////////////////////////////////////////////////////
// DecodePool implementation.
///////////////////////////////////////////////////////////////////////////////
/* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
/* static */ uint32_t DecodePool::sNumCores = 0;
NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
struct Work
{
enum class Type {
TASK,
SHUTDOWN
} mType;
RefPtr<IDecodingTask> mTask;
};
class DecodePoolImpl
{
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(DecodePoolImpl)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodePoolImpl)
DecodePoolImpl()
: mMonitor("DecodePoolImpl")
, mShuttingDown(false)
{ }
/// Shut down the provided decode pool thread.
static void ShutdownThread(nsIThread* aThisThread)
{
// Threads have to be shut down from another thread, so we'll ask the
// main thread to do it for us.
NS_DispatchToMainThread(NewRunnableMethod(aThisThread, &nsIThread::Shutdown));
}
/**
* Requests shutdown. New work items will be dropped on the floor, and all
* decode pool threads will be shut down once existing work items have been
* processed.
*/
void RequestShutdown()
{
MonitorAutoLock lock(mMonitor);
mShuttingDown = true;
mMonitor.NotifyAll();
}
/// Pushes a new decode work item.
void PushWork(IDecodingTask* aTask)
{
MOZ_ASSERT(aTask);
RefPtr<IDecodingTask> task(aTask);
MonitorAutoLock lock(mMonitor);
if (mShuttingDown) {
// Drop any new work on the floor if we're shutting down.
return;
}
if (task->Priority() == TaskPriority::eHigh) {
mHighPriorityQueue.AppendElement(Move(task));
} else {
mLowPriorityQueue.AppendElement(Move(task));
}
mMonitor.Notify();
}
/// Pops a new work item, blocking if necessary.
Work PopWork()
{
MonitorAutoLock lock(mMonitor);
do {
if (!mHighPriorityQueue.IsEmpty()) {
return PopWorkFromQueue(mHighPriorityQueue);
}
if (!mLowPriorityQueue.IsEmpty()) {
return PopWorkFromQueue(mLowPriorityQueue);
}
if (mShuttingDown) {
Work work;
work.mType = Work::Type::SHUTDOWN;
return work;
}
// Nothing to do; block until some work is available.
mMonitor.Wait();
} while (true);
}
nsresult CreateThread(nsIThread** aThread, nsIRunnable* aInitialEvent)
{
return NS_NewNamedThread(mThreadNaming.GetNextThreadName("ImgDecoder"),
aThread, aInitialEvent);
}
private:
~DecodePoolImpl() { }
Work PopWorkFromQueue(nsTArray<RefPtr<IDecodingTask>>& aQueue)
{
Work work;
work.mType = Work::Type::TASK;
work.mTask = aQueue.LastElement().forget();
aQueue.RemoveElementAt(aQueue.Length() - 1);
return work;
}
nsThreadPoolNaming mThreadNaming;
// mMonitor guards the queues and mShuttingDown.
Monitor mMonitor;
nsTArray<RefPtr<IDecodingTask>> mHighPriorityQueue;
nsTArray<RefPtr<IDecodingTask>> mLowPriorityQueue;
bool mShuttingDown;
};
class DecodePoolWorker : public Runnable
{
public:
explicit DecodePoolWorker(DecodePoolImpl* aImpl)
: mImpl(aImpl)
{ }
NS_IMETHOD Run() override
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIThread> thisThread;
nsThreadManager::get().GetCurrentThread(getter_AddRefs(thisThread));
do {
Work work = mImpl->PopWork();
switch (work.mType) {
case Work::Type::TASK:
work.mTask->Run();
break;
case Work::Type::SHUTDOWN:
DecodePoolImpl::ShutdownThread(thisThread);
#ifdef MOZ_ENABLE_PROFILER_SPS
profiler_unregister_thread();
#endif // MOZ_ENABLE_PROFILER_SPS
return NS_OK;
default:
MOZ_ASSERT_UNREACHABLE("Unknown work type");
}
} while (true);
MOZ_ASSERT_UNREACHABLE("Exiting thread without Work::Type::SHUTDOWN");
return NS_OK;
}
private:
RefPtr<DecodePoolImpl> mImpl;
};
/* static */ void
DecodePool::Initialize()
{
MOZ_ASSERT(NS_IsMainThread());
sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
DecodePool::Singleton();
}
/* static */ DecodePool*
DecodePool::Singleton()
{
if (!sSingleton) {
MOZ_ASSERT(NS_IsMainThread());
sSingleton = new DecodePool();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
/* static */ uint32_t
DecodePool::NumberOfCores()
{
return sNumCores;
}
DecodePool::DecodePool()
: mImpl(new DecodePoolImpl)
, mMutex("image::DecodePool")
{
// Determine the number of threads we want.
int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
uint32_t limit;
if (prefLimit <= 0) {
int32_t numCores = NumberOfCores();
if (numCores <= 1) {
limit = 1;
} else if (numCores == 2) {
// On an otherwise mostly idle system, having two image decoding threads
// doubles decoding performance, so it's worth doing on dual-core devices,
// even if under load we can't actually get that level of parallelism.
limit = 2;
} else {
limit = numCores - 1;
}
} else {
limit = static_cast<uint32_t>(prefLimit);
}
if (limit > 32) {
limit = 32;
}
// The parent process where there are content processes doesn't need as many
// threads for decoding images.
if (limit > 4 && XRE_IsParentProcess() && BrowserTabsRemoteAutostart()) {
limit = 4;
}
// Initialize the thread pool.
for (uint32_t i = 0 ; i < limit ; ++i) {
nsCOMPtr<nsIRunnable> worker = new DecodePoolWorker(mImpl);
nsCOMPtr<nsIThread> thread;
nsresult rv = mImpl->CreateThread(getter_AddRefs(thread), worker);
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && thread,
"Should successfully create image decoding threads");
mThreads.AppendElement(Move(thread));
}
// Initialize the I/O thread.
nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
"Should successfully create image I/O thread");
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
if (obsSvc) {
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
}
}
DecodePool::~DecodePool()
{
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
}
NS_IMETHODIMP
DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
{
MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
nsTArray<nsCOMPtr<nsIThread>> threads;
nsCOMPtr<nsIThread> ioThread;
{
MutexAutoLock lock(mMutex);
threads.SwapElements(mThreads);
ioThread.swap(mIOThread);
}
mImpl->RequestShutdown();
for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
threads[i]->Shutdown();
}
if (ioThread) {
ioThread->Shutdown();
}
return NS_OK;
}
void
DecodePool::AsyncRun(IDecodingTask* aTask)
{
MOZ_ASSERT(aTask);
mImpl->PushWork(aTask);
}
bool
DecodePool::SyncRunIfPreferred(IDecodingTask* aTask)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTask);
if (aTask->ShouldPreferSyncRun()) {
aTask->Run();
return true;
}
AsyncRun(aTask);
return false;
}
void
DecodePool::SyncRunIfPossible(IDecodingTask* aTask)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTask);
aTask->Run();
}
already_AddRefed<nsIEventTarget>
DecodePool::GetIOEventTarget()
{
MutexAutoLock threadPoolLock(mMutex);
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mIOThread);
return target.forget();
}
} // namespace image
} // namespace mozilla