/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "CanvasThread.h" #include "mozilla/SharedThreadPool.h" #include "nsThreadUtils.h" #include "prsystem.h" bool NS_IsInCanvasThreadOrWorker() { return mozilla::layers::CanvasThreadHolder::IsInCanvasThreadOrWorker(); } namespace mozilla { namespace layers { StaticDataMutex> CanvasThreadHolder::sCanvasThreadHolder("sCanvasThreadHolder"); CanvasThreadHolder::CanvasThreadHolder( already_AddRefed aCanvasThread, already_AddRefed aCanvasWorkers) : mCanvasThread(aCanvasThread), mCanvasWorkers(aCanvasWorkers), mCompositorThreadKeepAlive(CompositorThreadHolder::GetSingleton()) { MOZ_ASSERT(NS_IsInCompositorThread()); MOZ_ASSERT(mCanvasThread); MOZ_ASSERT(mCanvasWorkers); } CanvasThreadHolder::~CanvasThreadHolder() { // Note we can't just use NS_IsInCompositorThread() here because // sCompositorThreadHolder might have already gone. MOZ_ASSERT( mCompositorThreadKeepAlive->GetCompositorThread()->IsOnCurrentThread()); } /* static */ already_AddRefed CanvasThreadHolder::EnsureCanvasThread() { MOZ_ASSERT(NS_IsInCompositorThread()); auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); if (!lockedCanvasThreadHolder.ref()) { nsCOMPtr canvasThread; nsresult rv = NS_CreateBackgroundTaskQueue("Canvas", getter_AddRefs(canvasThread)); NS_ENSURE_SUCCESS(rv, nullptr); // Given that the canvas workers are receiving instructions from // content processes, it probably doesn't make sense to have more than // half the number of processors doing canvas drawing. We set the // lower limit to 2, so that even on single processor systems, if // there is more than one window with canvas drawing, the OS can // manage the load between them. uint32_t threadLimit = std::max(2, PR_GetNumberOfProcessors() / 2); nsCOMPtr canvasWorkers = SharedThreadPool::Get("CanvasWorkers"_ns, threadLimit); if (!canvasWorkers) { return nullptr; } lockedCanvasThreadHolder.ref() = new CanvasThreadHolder(canvasThread.forget(), canvasWorkers.forget()); } return do_AddRef(lockedCanvasThreadHolder.ref()); } /* static */ void CanvasThreadHolder::ReleaseOnCompositorThread( already_AddRefed aCanvasThreadHolder) { RefPtr canvasThreadHolder = aCanvasThreadHolder; auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); lockedCanvasThreadHolder.ref() ->mCompositorThreadKeepAlive->GetCompositorThread() ->Dispatch(NS_NewRunnableFunction( "CanvasThreadHolder::StaticRelease", [canvasThreadHolder = std::move(canvasThreadHolder)]() mutable { RefPtr threadHolder = canvasThreadHolder.forget(); threadHolder = nullptr; auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); if (lockedCanvasThreadHolder.ref()->mRefCnt == 1) { lockedCanvasThreadHolder.ref() = nullptr; } })); } /* static */ bool CanvasThreadHolder::IsInCanvasThread() { auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); return lockedCanvasThreadHolder.ref() && lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread(); } /* static */ bool CanvasThreadHolder::IsInCanvasWorker() { auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); return lockedCanvasThreadHolder.ref() && lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread(); } /* static */ bool CanvasThreadHolder::IsInCanvasThreadOrWorker() { auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); return lockedCanvasThreadHolder.ref() && (lockedCanvasThreadHolder.ref()->mCanvasWorkers->IsOnCurrentThread() || lockedCanvasThreadHolder.ref()->mCanvasThread->IsOnCurrentThread()); } /* static */ void CanvasThreadHolder::MaybeDispatchToCanvasThread( already_AddRefed aRunnable) { auto lockedCanvasThreadHolder = sCanvasThreadHolder.Lock(); if (!lockedCanvasThreadHolder.ref()) { // There is no canvas thread just release the runnable. nsCOMPtr runnable = aRunnable; return; } lockedCanvasThreadHolder.ref()->mCanvasThread->Dispatch(std::move(aRunnable)); } void CanvasThreadHolder::DispatchToCanvasThread( already_AddRefed aRunnable) { mCanvasThread->Dispatch(std::move(aRunnable)); } already_AddRefed CanvasThreadHolder::CreateWorkerTaskQueue() { return TaskQueue::Create(do_AddRef(mCanvasWorkers), "CanvasWorker").forget(); } } // namespace layers } // namespace mozilla