gecko-dev/image/DecodePool.cpp

352 lines
8.2 KiB
C++
Raw Normal View History

/* -*- 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("DecodePoolImpl::ShutdownThread",
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_GECKO_PROFILER
profiler_unregister_thread();
#endif // MOZ_GECKO_PROFILER
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:
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 05:24:48 +00:00
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