зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1886022 - Refactor canvas shutdown to account for process crashes. r=gfx-reviewers,lsalzman
We previously refactor canvas shutdown to account for the fact that they needed to be shutdown in conjunction with the DOM worker reference kept alive by the CanvasManagerChild. Unfortunately if the compositor process crashes, or otherwise the CanvasManagerChild actor is torn down, we also prematurely shutdown the canvas when it would previously fallback to Skia in the content process. This patch abstracts out canvas shutdown into the CanvasShutdownManager which has the owning reference to the ThreadSafeWorkerRef. It corrects a similar bug on the main thread as well for HTMLCanvasElement. Differential Revision: https://phabricator.services.mozilla.com/D204988
This commit is contained in:
Родитель
47c14cf7a4
Коммит
0a149a309c
|
@ -24,7 +24,7 @@
|
|||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "mozilla/dom/VideoFrame.h"
|
||||
#include "mozilla/gfx/CanvasManagerChild.h"
|
||||
#include "mozilla/gfx/CanvasShutdownManager.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
|
@ -872,41 +872,6 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
|
||||
|
||||
class CanvasShutdownObserver final : public nsIObserver {
|
||||
public:
|
||||
explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
|
||||
: mCanvas(aCanvas) {}
|
||||
|
||||
void OnShutdown() {
|
||||
if (!mCanvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCanvas = nullptr;
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
private:
|
||||
~CanvasShutdownObserver() = default;
|
||||
|
||||
CanvasRenderingContext2D* mCanvas;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
CanvasShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
mCanvas->OnShutdown();
|
||||
OnShutdown();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
|
||||
|
||||
|
@ -1243,14 +1208,8 @@ void CanvasRenderingContext2D::OnShutdown() {
|
|||
}
|
||||
|
||||
bool CanvasRenderingContext2D::AddShutdownObserver() {
|
||||
auto* const canvasManager = CanvasManagerChild::Get();
|
||||
auto* const canvasManager = CanvasShutdownManager::Get();
|
||||
if (NS_WARN_IF(!canvasManager)) {
|
||||
if (NS_IsMainThread()) {
|
||||
mShutdownObserver = new CanvasShutdownObserver(this);
|
||||
nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
mHasShutdown = true;
|
||||
return false;
|
||||
}
|
||||
|
@ -1260,13 +1219,7 @@ bool CanvasRenderingContext2D::AddShutdownObserver() {
|
|||
}
|
||||
|
||||
void CanvasRenderingContext2D::RemoveShutdownObserver() {
|
||||
if (mShutdownObserver) {
|
||||
mShutdownObserver->OnShutdown();
|
||||
mShutdownObserver = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const canvasManager = CanvasManagerChild::MaybeGet();
|
||||
auto* const canvasManager = CanvasShutdownManager::MaybeGet();
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -846,7 +846,6 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
|||
// Whether or not we have already shutdown.
|
||||
bool mHasShutdown = false;
|
||||
|
||||
RefPtr<CanvasShutdownObserver> mShutdownObserver;
|
||||
bool AddShutdownObserver();
|
||||
void RemoveShutdownObserver();
|
||||
bool AlreadyShutDown() const { return mHasShutdown; }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRef.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/CanvasShutdownManager.h"
|
||||
#include "mozilla/gfx/Swizzle.h"
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
#include "mozilla/layers/ActiveResource.h"
|
||||
|
@ -30,7 +31,10 @@ MOZ_THREAD_LOCAL(CanvasManagerChild*) CanvasManagerChild::sLocalManager;
|
|||
|
||||
Atomic<uint32_t> CanvasManagerChild::sNextId(1);
|
||||
|
||||
CanvasManagerChild::CanvasManagerChild(uint32_t aId) : mId(aId) {}
|
||||
CanvasManagerChild::CanvasManagerChild(ThreadSafeWorkerRef* aWorkerRef,
|
||||
uint32_t aId)
|
||||
: mWorkerRef(aWorkerRef), mId(aId) {}
|
||||
|
||||
CanvasManagerChild::~CanvasManagerChild() = default;
|
||||
|
||||
void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) {
|
||||
|
@ -42,11 +46,6 @@ void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) {
|
|||
}
|
||||
|
||||
void CanvasManagerChild::DestroyInternal() {
|
||||
std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas);
|
||||
for (const auto& i : activeCanvas) {
|
||||
i->OnShutdown();
|
||||
}
|
||||
|
||||
if (mActiveResourceTracker) {
|
||||
mActiveResourceTracker->AgeAllGenerations();
|
||||
mActiveResourceTracker.reset();
|
||||
|
@ -67,12 +66,6 @@ void CanvasManagerChild::Destroy() {
|
|||
}
|
||||
|
||||
/* static */ void CanvasManagerChild::Shutdown() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// The worker threads should destroy their own CanvasManagerChild instances
|
||||
// during their shutdown sequence. We just need to take care of the main
|
||||
// thread. We need to init here because we may have never created a
|
||||
// CanvasManagerChild for the main thread in the first place.
|
||||
if (sLocalManager.init()) {
|
||||
RefPtr<CanvasManagerChild> manager = sLocalManager.get();
|
||||
if (manager) {
|
||||
|
@ -103,6 +96,11 @@ void CanvasManagerChild::Destroy() {
|
|||
return managerWeak;
|
||||
}
|
||||
|
||||
auto* shutdownManager = CanvasShutdownManager::Get();
|
||||
if (NS_WARN_IF(!shutdownManager)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We are only used on the main thread, or on worker threads.
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT_IF(!worker, NS_IsMainThread());
|
||||
|
@ -121,29 +119,8 @@ void CanvasManagerChild::Destroy() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
auto manager = MakeRefPtr<CanvasManagerChild>(sNextId++);
|
||||
|
||||
if (worker) {
|
||||
// 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, "CanvasManager", [manager]() { manager->Destroy(); });
|
||||
if (NS_WARN_IF(!workerRef)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
manager->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
|
||||
} else if (NS_IsMainThread()) {
|
||||
if (NS_WARN_IF(
|
||||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto manager = MakeRefPtr<CanvasManagerChild>(shutdownManager->GetWorkerRef(),
|
||||
sNextId++);
|
||||
if (NS_WARN_IF(!childEndpoint.Bind(manager))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -175,16 +152,6 @@ void CanvasManagerChild::Destroy() {
|
|||
return sLocalManager.get();
|
||||
}
|
||||
|
||||
void CanvasManagerChild::AddShutdownObserver(
|
||||
dom::CanvasRenderingContext2D* aCanvas) {
|
||||
mActiveCanvas.insert(aCanvas);
|
||||
}
|
||||
|
||||
void CanvasManagerChild::RemoveShutdownObserver(
|
||||
dom::CanvasRenderingContext2D* aCanvas) {
|
||||
mActiveCanvas.erase(aCanvas);
|
||||
}
|
||||
|
||||
void CanvasManagerChild::EndCanvasTransaction() {
|
||||
if (!mCanvasChild) {
|
||||
return;
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "mozilla/gfx/PCanvasManagerChild.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include <set>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -35,7 +34,8 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
|||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CanvasManagerChild, override);
|
||||
|
||||
explicit CanvasManagerChild(uint32_t aId);
|
||||
explicit CanvasManagerChild(dom::ThreadSafeWorkerRef* aWorkerRef,
|
||||
uint32_t aId);
|
||||
uint32_t Id() const { return mId; }
|
||||
already_AddRefed<DataSourceSurface> GetSnapshot(
|
||||
uint32_t aManagerId, int32_t aProtocolId,
|
||||
|
@ -49,9 +49,6 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
|||
static bool CreateParent(
|
||||
mozilla::ipc::Endpoint<PCanvasManagerParent>&& aEndpoint);
|
||||
|
||||
void AddShutdownObserver(dom::CanvasRenderingContext2D* aCanvas);
|
||||
void RemoveShutdownObserver(dom::CanvasRenderingContext2D* aCanvas);
|
||||
|
||||
bool IsCanvasActive() { return mActive; }
|
||||
void EndCanvasTransaction();
|
||||
void ClearCachedResources();
|
||||
|
@ -73,7 +70,6 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
|||
RefPtr<layers::CanvasChild> mCanvasChild;
|
||||
RefPtr<webgpu::WebGPUChild> mWebGPUChild;
|
||||
UniquePtr<layers::ActiveResourceTracker> mActiveResourceTracker;
|
||||
std::set<dom::CanvasRenderingContext2D*> mActiveCanvas;
|
||||
const uint32_t mId;
|
||||
bool mActive = true;
|
||||
bool mBlocked = false;
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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/gfx/CanvasManagerChild.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla::gfx {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
return manager;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
if (NS_WARN_IF(
|
||||
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CanvasShutdownManager* manager = new CanvasShutdownManager();
|
||||
sLocalManager.set(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);
|
||||
}
|
||||
|
||||
} // namespace mozilla::gfx
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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/. */
|
||||
#ifndef _include_gfx_ipc_CanvasShutdownManager_h__
|
||||
#define _include_gfx_ipc_CanvasShutdownManager_h__
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include <set>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class CanvasRenderingContext2D;
|
||||
class StrongWorkerRef;
|
||||
class ThreadSafeWorkerRef;
|
||||
} // namespace dom
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class CanvasShutdownManager final {
|
||||
public:
|
||||
static CanvasShutdownManager* Get();
|
||||
static CanvasShutdownManager* MaybeGet();
|
||||
static void Shutdown();
|
||||
|
||||
dom::ThreadSafeWorkerRef* GetWorkerRef() const { return mWorkerRef; }
|
||||
void AddShutdownObserver(dom::CanvasRenderingContext2D* aCanvas);
|
||||
void RemoveShutdownObserver(dom::CanvasRenderingContext2D* aCanvas);
|
||||
|
||||
private:
|
||||
explicit CanvasShutdownManager(dom::StrongWorkerRef* aWorkerRef);
|
||||
CanvasShutdownManager();
|
||||
~CanvasShutdownManager();
|
||||
void Destroy();
|
||||
|
||||
RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef;
|
||||
std::set<dom::CanvasRenderingContext2D*> mActiveCanvas;
|
||||
static MOZ_THREAD_LOCAL(CanvasShutdownManager*) sLocalManager;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _include_gfx_ipc_CanvasShutdownManager_h__
|
|
@ -13,6 +13,7 @@ EXPORTS.mozilla.gfx += [
|
|||
"CanvasManagerChild.h",
|
||||
"CanvasManagerParent.h",
|
||||
"CanvasRenderThread.h",
|
||||
"CanvasShutdownManager.h",
|
||||
"CrossProcessPaint.h",
|
||||
"FileHandleWrapper.h",
|
||||
"GPUChild.h",
|
||||
|
@ -42,6 +43,7 @@ UNIFIED_SOURCES += [
|
|||
"CanvasManagerChild.cpp",
|
||||
"CanvasManagerParent.cpp",
|
||||
"CanvasRenderThread.cpp",
|
||||
"CanvasShutdownManager.cpp",
|
||||
"CompositorSession.cpp",
|
||||
"CompositorWidgetVsyncObserver.cpp",
|
||||
"CrossProcessPaint.cpp",
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "mozilla/gfx/GPUProcessManager.h"
|
||||
#include "mozilla/gfx/GraphicsMessages.h"
|
||||
#include "mozilla/gfx/CanvasManagerChild.h"
|
||||
#include "mozilla/gfx/CanvasRenderThread.h"
|
||||
#include "mozilla/gfx/CanvasShutdownManager.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/EnumTypeTraits.h"
|
||||
|
@ -1343,7 +1343,7 @@ void gfxPlatform::ShutdownLayersIPC() {
|
|||
|
||||
if (XRE_IsContentProcess()) {
|
||||
gfx::VRManagerChild::ShutDown();
|
||||
gfx::CanvasManagerChild::Shutdown();
|
||||
gfx::CanvasShutdownManager::Shutdown();
|
||||
// cf bug 1215265.
|
||||
if (StaticPrefs::layers_child_process_shutdown()) {
|
||||
layers::CompositorManagerChild::Shutdown();
|
||||
|
@ -1354,7 +1354,7 @@ void gfxPlatform::ShutdownLayersIPC() {
|
|||
VideoBridgeParent::Shutdown();
|
||||
RDDProcessManager::RDDProcessShutdown();
|
||||
gfx::VRManagerChild::ShutDown();
|
||||
gfx::CanvasManagerChild::Shutdown();
|
||||
gfx::CanvasShutdownManager::Shutdown();
|
||||
layers::CompositorManagerChild::Shutdown();
|
||||
layers::ImageBridgeChild::ShutDown();
|
||||
// This could be running on either the Compositor thread, the Renderer
|
||||
|
|
Загрузка…
Ссылка в новой задаче