diff options
Diffstat (limited to 'gfx/ipc/CanvasShutdownManager.cpp')
-rw-r--r-- | gfx/ipc/CanvasShutdownManager.cpp | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/gfx/ipc/CanvasShutdownManager.cpp b/gfx/ipc/CanvasShutdownManager.cpp new file mode 100644 index 0000000000..d7fc96d255 --- /dev/null +++ b/gfx/ipc/CanvasShutdownManager.cpp @@ -0,0 +1,179 @@ +/* -*- 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 "CanvasShutdownManager.h" +#include "mozilla/AppShutdown.h" +#include "mozilla/dom/CanvasRenderingContext2D.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/WorkerRunnable.h" +#include "mozilla/gfx/CanvasManagerChild.h" + +using namespace mozilla::dom; + +namespace mozilla::gfx { + +StaticMutex CanvasShutdownManager::sManagersMutex; +std::set<CanvasShutdownManager*> CanvasShutdownManager::sManagers; + +// The owning thread will tell us to close when it is shutdown, either via +// CanvasShutdownManager::Shutdown for the main thread, or via a shutdown +// callback from ThreadSafeWorkerRef for worker threads. +MOZ_THREAD_LOCAL(CanvasShutdownManager*) CanvasShutdownManager::sLocalManager; + +CanvasShutdownManager::CanvasShutdownManager(StrongWorkerRef* aWorkerRef) + : mWorkerRef(new ThreadSafeWorkerRef(aWorkerRef)) {} + +CanvasShutdownManager::CanvasShutdownManager() = default; +CanvasShutdownManager::~CanvasShutdownManager() = default; + +void CanvasShutdownManager::Destroy() { + std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas); + for (const auto& i : activeCanvas) { + i->OnShutdown(); + } + + CanvasManagerChild::Shutdown(); + mWorkerRef = nullptr; +} + +/* static */ void CanvasShutdownManager::Shutdown() { + auto* manager = MaybeGet(); + if (!manager) { + return; + } + + { + StaticMutexAutoLock lock(sManagersMutex); + sManagers.erase(manager); + } + + sLocalManager.set(nullptr); + manager->Destroy(); + delete manager; +} + +/* static */ CanvasShutdownManager* CanvasShutdownManager::MaybeGet() { + if (NS_WARN_IF(!sLocalManager.init())) { + return nullptr; + } + + return sLocalManager.get(); +} + +/* static */ CanvasShutdownManager* CanvasShutdownManager::Get() { + if (NS_WARN_IF(!sLocalManager.init())) { + return nullptr; + } + + CanvasShutdownManager* managerWeak = sLocalManager.get(); + if (managerWeak) { + return managerWeak; + } + + if (WorkerPrivate* worker = GetCurrentThreadWorkerPrivate()) { + // The ThreadSafeWorkerRef will let us know when the worker is shutting + // down. This will let us clear our threadlocal reference and close the + // actor. We rely upon an explicit shutdown for the main thread. + RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( + worker, "CanvasShutdownManager", []() { Shutdown(); }); + if (NS_WARN_IF(!workerRef)) { + return nullptr; + } + + CanvasShutdownManager* manager = new CanvasShutdownManager(workerRef); + sLocalManager.set(manager); + + StaticMutexAutoLock lock(sManagersMutex); + sManagers.insert(manager); + return manager; + } + + if (NS_IsMainThread()) { + if (NS_WARN_IF( + AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) { + return nullptr; + } + + CanvasShutdownManager* manager = new CanvasShutdownManager(); + sLocalManager.set(manager); + + StaticMutexAutoLock lock(sManagersMutex); + sManagers.insert(manager); + return manager; + } + + MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!"); + return nullptr; +} + +void CanvasShutdownManager::AddShutdownObserver( + dom::CanvasRenderingContext2D* aCanvas) { + mActiveCanvas.insert(aCanvas); +} + +void CanvasShutdownManager::RemoveShutdownObserver( + dom::CanvasRenderingContext2D* aCanvas) { + mActiveCanvas.erase(aCanvas); +} + +void CanvasShutdownManager::OnRemoteCanvasLost() { + // Note that the canvas cannot do anything that mutates our state. It will + // dispatch for anything that risks re-entrancy. + for (const auto& canvas : mActiveCanvas) { + canvas->OnRemoteCanvasLost(); + } +} + +void CanvasShutdownManager::OnRemoteCanvasRestored() { + // Note that the canvas cannot do anything that mutates our state. It will + // dispatch for anything that risks re-entrancy. + for (const auto& canvas : mActiveCanvas) { + canvas->OnRemoteCanvasRestored(); + } +} + +/* static */ void CanvasShutdownManager::MaybeRestoreRemoteCanvas() { + // Calling Get will recreate the CanvasManagerChild, which in turn will + // cause us to call OnRemoteCanvasRestore upon success. + if (CanvasShutdownManager* manager = MaybeGet()) { + if (!manager->mActiveCanvas.empty()) { + CanvasManagerChild::Get(); + } + } +} + +/* static */ void CanvasShutdownManager::OnCompositorManagerRestored() { + MOZ_ASSERT(NS_IsMainThread()); + + class RestoreRunnable final : public WorkerRunnable { + public: + explicit RestoreRunnable(WorkerPrivate* aWorkerPrivate) + : WorkerRunnable(aWorkerPrivate, + "CanvasShutdownManager::RestoreRunnable") {} + + bool WorkerRun(JSContext*, WorkerPrivate*) override { + MaybeRestoreRemoteCanvas(); + return true; + } + }; + + // We can restore the main thread canvases immediately. + MaybeRestoreRemoteCanvas(); + + // And dispatch to restore any DOM worker canvases. This is safe because we + // remove the manager from sManagers before clearing mWorkerRef during DOM + // worker shutdown. + StaticMutexAutoLock lock(sManagersMutex); + for (const auto& manager : sManagers) { + if (manager->mWorkerRef) { + auto task = MakeRefPtr<RestoreRunnable>(manager->mWorkerRef->Private()); + task->Dispatch(); + } + } +} + +} // namespace mozilla::gfx |