mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 20:01:50 +00:00
Bug 1855742 - Part 5. Refactor canvas shutdown to be synchronized with CanvasManagerChild. r=lsalzman
Because we cannot control the ordering of shutdown notification from WorkerRef, we need to choose one centralized point to begin shutdown of the main thread or the owning DOM worker. Since there are other objects owned by CanvasManagerChild, we also switch to a ThreadSafeWorkerRef. Differential Revision: https://phabricator.services.mozilla.com/D195120
This commit is contained in:
parent
fca521d398
commit
696dee1a1a
@ -24,6 +24,7 @@
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "mozilla/dom/VideoFrame.h"
|
||||
#include "mozilla/gfx/CanvasManagerChild.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
@ -1130,7 +1131,13 @@ CanvasRenderingContext2D::~CanvasRenderingContext2D() {
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::Initialize() { AddShutdownObserver(); }
|
||||
nsresult CanvasRenderingContext2D::Initialize() {
|
||||
if (NS_WARN_IF(!AddShutdownObserver())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject* CanvasRenderingContext2D::WrapObject(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
||||
@ -1218,8 +1225,6 @@ void CanvasRenderingContext2D::ResetBitmap(bool aFreeBuffer) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::OnShutdown() {
|
||||
mShutdownObserver = nullptr;
|
||||
|
||||
RefPtr<PersistentBufferProvider> provider = mBufferProvider;
|
||||
|
||||
ResetBitmap();
|
||||
@ -1227,21 +1232,44 @@ void CanvasRenderingContext2D::OnShutdown() {
|
||||
if (provider) {
|
||||
provider->OnShutdown();
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
mOffscreenCanvas->Destroy();
|
||||
}
|
||||
|
||||
mHasShutdown = true;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::AddShutdownObserver() {
|
||||
MOZ_ASSERT(!mShutdownObserver);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
bool CanvasRenderingContext2D::AddShutdownObserver() {
|
||||
auto* const canvasManager = CanvasManagerChild::Get();
|
||||
if (NS_WARN_IF(!canvasManager)) {
|
||||
if (NS_IsMainThread()) {
|
||||
mShutdownObserver = new CanvasShutdownObserver(this);
|
||||
nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
mShutdownObserver = new CanvasShutdownObserver(this);
|
||||
nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
|
||||
mHasShutdown = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
canvasManager->AddShutdownObserver(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::RemoveShutdownObserver() {
|
||||
if (mShutdownObserver) {
|
||||
mShutdownObserver->OnShutdown();
|
||||
mShutdownObserver = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const canvasManager = CanvasManagerChild::MaybeGet();
|
||||
if (!canvasManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvasManager->RemoveShutdownObserver(this);
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::SetStyleFromString(const nsACString& aStr,
|
||||
@ -1864,6 +1892,10 @@ void CanvasRenderingContext2D::ReturnTarget(bool aForceReset) {
|
||||
NS_IMETHODIMP
|
||||
CanvasRenderingContext2D::InitializeWithDrawTarget(
|
||||
nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) {
|
||||
if (NS_WARN_IF(!AddShutdownObserver())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RemovePostRefreshObserver();
|
||||
mDocShell = aShell;
|
||||
AddPostRefreshObserverIfNecessary();
|
||||
|
@ -464,7 +464,7 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
||||
* Gets the pres shell from either the canvas element or the doc shell
|
||||
*/
|
||||
PresShell* GetPresShell() final;
|
||||
void Initialize() override;
|
||||
nsresult Initialize() override;
|
||||
NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
|
||||
NS_IMETHOD InitializeWithDrawTarget(
|
||||
nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) override;
|
||||
@ -565,7 +565,7 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
||||
virtual UniquePtr<uint8_t[]> GetImageBuffer(
|
||||
int32_t* out_format, gfx::IntSize* out_imageSize) override;
|
||||
|
||||
virtual void OnShutdown();
|
||||
void OnShutdown();
|
||||
|
||||
/**
|
||||
* Update CurrentState().filter with the filter description for
|
||||
@ -831,11 +831,13 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
||||
// Whether the application expects to use operations that perform poorly with
|
||||
// acceleration.
|
||||
bool mWillReadFrequently = false;
|
||||
// Whether or not we have already shutdown.
|
||||
bool mHasShutdown = false;
|
||||
|
||||
RefPtr<CanvasShutdownObserver> mShutdownObserver;
|
||||
virtual void AddShutdownObserver();
|
||||
virtual void RemoveShutdownObserver();
|
||||
virtual bool AlreadyShutDown() const { return !mShutdownObserver; }
|
||||
bool AddShutdownObserver();
|
||||
void RemoveShutdownObserver();
|
||||
bool AlreadyShutDown() const { return mHasShutdown; }
|
||||
|
||||
/**
|
||||
* Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
|
||||
|
@ -184,7 +184,9 @@ CanvasRenderingContextHelper::CreateContextHelper(
|
||||
}
|
||||
MOZ_ASSERT(ret);
|
||||
|
||||
ret->Initialize();
|
||||
if (NS_WARN_IF(NS_FAILED(ret->Initialize()))) {
|
||||
return nullptr;
|
||||
}
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
@ -206,6 +208,7 @@ already_AddRefed<nsISupports> CanvasRenderingContextHelper::GetOrCreateContext(
|
||||
RefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContext(aContextType);
|
||||
if (!context) {
|
||||
aRv.ThrowUnknownError("Failed to create context");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -66,11 +66,15 @@ OffscreenCanvas::OffscreenCanvas(
|
||||
mDisplay(aDisplay) {}
|
||||
|
||||
OffscreenCanvas::~OffscreenCanvas() {
|
||||
Destroy();
|
||||
NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
|
||||
mExpandedReader.forget());
|
||||
}
|
||||
|
||||
void OffscreenCanvas::Destroy() {
|
||||
if (mDisplay) {
|
||||
mDisplay->DestroyCanvas();
|
||||
}
|
||||
NS_ReleaseOnMainThread("OffscreenCanvas::mExpandedReader",
|
||||
mExpandedReader.forget());
|
||||
}
|
||||
|
||||
JSObject* OffscreenCanvas::WrapObject(JSContext* aCx,
|
||||
@ -270,6 +274,9 @@ already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
OffscreenCanvas::CreateContext(CanvasContextType aContextType) {
|
||||
RefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CanvasRenderingContextHelper::CreateContext(aContextType);
|
||||
if (NS_WARN_IF(!ret)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->SetOffscreenCanvas(this);
|
||||
return ret.forget();
|
||||
|
@ -79,6 +79,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
|
||||
layers::TextureType aTextureType,
|
||||
already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay);
|
||||
|
||||
void Destroy();
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return GetOwnerGlobal(); }
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
|
@ -8,36 +8,11 @@
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/dom/OffscreenCanvasRenderingContext2DBinding.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerRef.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class OffscreenCanvasShutdownObserver final {
|
||||
NS_INLINE_DECL_REFCOUNTING(OffscreenCanvasShutdownObserver)
|
||||
|
||||
public:
|
||||
explicit OffscreenCanvasShutdownObserver(
|
||||
OffscreenCanvasRenderingContext2D* aOwner)
|
||||
: mOwner(aOwner) {}
|
||||
|
||||
void OnShutdown() {
|
||||
if (mOwner) {
|
||||
mOwner->OnShutdown();
|
||||
mOwner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearOwner() { mOwner = nullptr; }
|
||||
|
||||
private:
|
||||
~OffscreenCanvasShutdownObserver() = default;
|
||||
|
||||
OffscreenCanvasRenderingContext2D* mOwner;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvasRenderingContext2D,
|
||||
CanvasRenderingContext2D)
|
||||
|
||||
@ -91,45 +66,6 @@ NS_IMETHODIMP OffscreenCanvasRenderingContext2D::InitializeWithDrawTarget(
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::AddShutdownObserver() {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
if (!workerPrivate) {
|
||||
// We may be using OffscreenCanvas on the main thread.
|
||||
CanvasRenderingContext2D::AddShutdownObserver();
|
||||
return;
|
||||
}
|
||||
|
||||
mOffscreenShutdownObserver =
|
||||
MakeAndAddRef<OffscreenCanvasShutdownObserver>(this);
|
||||
mWorkerRef = WeakWorkerRef::Create(
|
||||
workerPrivate,
|
||||
[observer = mOffscreenShutdownObserver] { observer->OnShutdown(); });
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::RemoveShutdownObserver() {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
if (!workerPrivate) {
|
||||
// We may be using OffscreenCanvas on the main thread.
|
||||
CanvasRenderingContext2D::RemoveShutdownObserver();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOffscreenShutdownObserver) {
|
||||
mOffscreenShutdownObserver->ClearOwner();
|
||||
}
|
||||
mOffscreenShutdownObserver = nullptr;
|
||||
mWorkerRef = nullptr;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::OnShutdown() {
|
||||
if (mOffscreenShutdownObserver) {
|
||||
mOffscreenShutdownObserver->ClearOwner();
|
||||
mOffscreenShutdownObserver = nullptr;
|
||||
}
|
||||
|
||||
CanvasRenderingContext2D::OnShutdown();
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::Commit(ErrorResult& aRv) {
|
||||
if (!mOffscreenCanvas->IsTransferredFromElement()) {
|
||||
return;
|
||||
|
@ -15,8 +15,6 @@ class nsIGlobalObject;
|
||||
|
||||
namespace mozilla::dom {
|
||||
class OffscreenCanvas;
|
||||
class OffscreenCanvasShutdownObserver;
|
||||
class WeakWorkerRef;
|
||||
|
||||
class OffscreenCanvasRenderingContext2D final
|
||||
: public CanvasRenderingContext2D {
|
||||
@ -39,27 +37,15 @@ class OffscreenCanvasRenderingContext2D final
|
||||
|
||||
void Commit(ErrorResult& aRv);
|
||||
|
||||
void OnShutdown() override;
|
||||
|
||||
NS_IMETHOD InitializeWithDrawTarget(
|
||||
nsIDocShell* aShell, NotNull<gfx::DrawTarget*> aTarget) override;
|
||||
|
||||
private:
|
||||
void AddShutdownObserver() override;
|
||||
void RemoveShutdownObserver() override;
|
||||
bool AlreadyShutDown() const override {
|
||||
return !mOffscreenShutdownObserver &&
|
||||
CanvasRenderingContext2D::AlreadyShutDown();
|
||||
}
|
||||
|
||||
void AddZoneWaitingForGC() override;
|
||||
void AddAssociatedMemory() override;
|
||||
void RemoveAssociatedMemory() override;
|
||||
|
||||
~OffscreenCanvasRenderingContext2D() override;
|
||||
|
||||
RefPtr<OffscreenCanvasShutdownObserver> mOffscreenShutdownObserver;
|
||||
RefPtr<WeakWorkerRef> mWorkerRef;
|
||||
};
|
||||
|
||||
size_t BindingJSObjectMallocBytes(OffscreenCanvasRenderingContext2D* aContext);
|
||||
|
@ -101,7 +101,7 @@ class nsICanvasRenderingContextInternal : public nsISupports,
|
||||
NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
|
||||
|
||||
// Initializes the canvas after the object is constructed.
|
||||
virtual void Initialize() {}
|
||||
virtual nsresult Initialize() { return NS_OK; }
|
||||
|
||||
// Initializes with an nsIDocShell and DrawTarget. The size is taken from the
|
||||
// DrawTarget.
|
||||
|
@ -535,6 +535,9 @@ HTMLCanvasElement::CreateContext(CanvasContextType aContextType) {
|
||||
// Note that the compositor backend will be LAYERS_NONE if there is no widget.
|
||||
RefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CreateContextHelper(aContextType, GetCompositorBackendType());
|
||||
if (NS_WARN_IF(!ret)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add Observer for webgl canvas.
|
||||
if (aContextType == CanvasContextType::WebGL1 ||
|
||||
|
@ -5,6 +5,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CanvasManagerChild.h"
|
||||
#include "mozilla/AppShutdown.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRef.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
@ -23,7 +25,7 @@ namespace mozilla::gfx {
|
||||
// The IPDL actor holds a strong reference to CanvasManagerChild which we use
|
||||
// to keep it alive. The owning thread will tell us to close when it is
|
||||
// shutdown, either via CanvasManagerChild::Shutdown for the main thread, or
|
||||
// via a shutdown callback from IPCWorkerRef for worker threads.
|
||||
// via a shutdown callback from ThreadSafeWorkerRef for worker threads.
|
||||
MOZ_THREAD_LOCAL(CanvasManagerChild*) CanvasManagerChild::sLocalManager;
|
||||
|
||||
Atomic<uint32_t> CanvasManagerChild::sNextId(1);
|
||||
@ -32,13 +34,19 @@ CanvasManagerChild::CanvasManagerChild(uint32_t aId) : mId(aId) {}
|
||||
CanvasManagerChild::~CanvasManagerChild() = default;
|
||||
|
||||
void CanvasManagerChild::ActorDestroy(ActorDestroyReason aReason) {
|
||||
DestroyInternal();
|
||||
if (sLocalManager.get() == this) {
|
||||
sLocalManager.set(nullptr);
|
||||
}
|
||||
mWorkerRef = nullptr;
|
||||
}
|
||||
|
||||
void CanvasManagerChild::Destroy() {
|
||||
void CanvasManagerChild::DestroyInternal() {
|
||||
std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas);
|
||||
for (const auto& i : activeCanvas) {
|
||||
i->OnShutdown();
|
||||
}
|
||||
|
||||
if (mActiveResourceTracker) {
|
||||
mActiveResourceTracker->AgeAllGenerations();
|
||||
mActiveResourceTracker.reset();
|
||||
@ -48,6 +56,10 @@ void CanvasManagerChild::Destroy() {
|
||||
mCanvasChild->Destroy();
|
||||
mCanvasChild = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasManagerChild::Destroy() {
|
||||
DestroyInternal();
|
||||
|
||||
// The caller has a strong reference. ActorDestroy will clear sLocalManager
|
||||
// and mWorkerRef.
|
||||
@ -112,14 +124,24 @@ void CanvasManagerChild::Destroy() {
|
||||
auto manager = MakeRefPtr<CanvasManagerChild>(sNextId++);
|
||||
|
||||
if (worker) {
|
||||
// The IPCWorkerRef 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.
|
||||
manager->mWorkerRef = IPCWorkerRef::Create(
|
||||
// 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(!manager->mWorkerRef)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!childEndpoint.Bind(manager))) {
|
||||
@ -145,6 +167,24 @@ void CanvasManagerChild::Destroy() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/* static */ CanvasManagerChild* CanvasManagerChild::MaybeGet() {
|
||||
if (!sLocalManager.initialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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,10 +10,12 @@
|
||||
#include "mozilla/gfx/PCanvasManagerChild.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include <set>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class IPCWorkerRef;
|
||||
class CanvasRenderingContext2D;
|
||||
class ThreadSafeWorkerRef;
|
||||
class WorkerPrivate;
|
||||
} // namespace dom
|
||||
|
||||
@ -42,10 +44,14 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
||||
void ActorDestroy(ActorDestroyReason aReason) override;
|
||||
|
||||
static CanvasManagerChild* Get();
|
||||
static CanvasManagerChild* MaybeGet();
|
||||
static void Shutdown();
|
||||
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();
|
||||
@ -60,12 +66,14 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
||||
|
||||
private:
|
||||
~CanvasManagerChild();
|
||||
void DestroyInternal();
|
||||
void Destroy();
|
||||
|
||||
RefPtr<mozilla::dom::IPCWorkerRef> mWorkerRef;
|
||||
RefPtr<mozilla::dom::ThreadSafeWorkerRef> mWorkerRef;
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user