CanvasShutdownManager.cpp (6180B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CanvasShutdownManager.h" 8 #include "mozilla/AppShutdown.h" 9 #include "mozilla/dom/CanvasRenderingContext2D.h" 10 #include "mozilla/dom/WorkerPrivate.h" 11 #include "mozilla/dom/WorkerRef.h" 12 #include "mozilla/dom/WorkerRunnable.h" 13 #include "mozilla/gfx/CanvasManagerChild.h" 14 #include "mozilla/layers/PersistentBufferProvider.h" 15 16 namespace mozilla::gfx { 17 18 using dom::CanvasRenderingContext2D; 19 using dom::StrongWorkerRef; 20 using dom::WorkerPrivate; 21 22 StaticMutex CanvasShutdownManager::sManagersMutex; 23 MOZ_RUNINIT std::set<CanvasShutdownManager*> CanvasShutdownManager::sManagers; 24 25 // The owning thread will tell us to close when it is shutdown, either via 26 // CanvasShutdownManager::Shutdown for the main thread, or via a shutdown 27 // callback from ThreadSafeWorkerRef for worker threads. 28 MOZ_THREAD_LOCAL(CanvasShutdownManager*) CanvasShutdownManager::sLocalManager; 29 30 CanvasShutdownManager::CanvasShutdownManager(StrongWorkerRef* aWorkerRef) 31 : mWorkerRef(new dom::ThreadSafeWorkerRef(aWorkerRef)) {} 32 33 CanvasShutdownManager::CanvasShutdownManager() = default; 34 CanvasShutdownManager::~CanvasShutdownManager() = default; 35 36 void CanvasShutdownManager::Destroy() { 37 std::set<CanvasRenderingContext2D*> activeCanvas = std::move(mActiveCanvas); 38 for (const auto& i : activeCanvas) { 39 i->OnShutdown(); 40 } 41 42 CanvasManagerChild::Shutdown(); 43 mWorkerRef = nullptr; 44 } 45 46 /* static */ void CanvasShutdownManager::Shutdown() { 47 auto* manager = MaybeGet(); 48 if (!manager) { 49 return; 50 } 51 52 { 53 StaticMutexAutoLock lock(sManagersMutex); 54 sManagers.erase(manager); 55 } 56 57 sLocalManager.set(nullptr); 58 manager->Destroy(); 59 delete manager; 60 } 61 62 /* static */ CanvasShutdownManager* CanvasShutdownManager::MaybeGet() { 63 if (NS_WARN_IF(!sLocalManager.init())) { 64 return nullptr; 65 } 66 67 return sLocalManager.get(); 68 } 69 70 /* static */ CanvasShutdownManager* CanvasShutdownManager::Get() { 71 if (NS_WARN_IF(!sLocalManager.init())) { 72 return nullptr; 73 } 74 75 CanvasShutdownManager* managerWeak = sLocalManager.get(); 76 if (managerWeak) { 77 return managerWeak; 78 } 79 80 if (WorkerPrivate* worker = dom::GetCurrentThreadWorkerPrivate()) { 81 // The ThreadSafeWorkerRef will let us know when the worker is shutting 82 // down. This will let us clear our threadlocal reference and close the 83 // actor. We rely upon an explicit shutdown for the main thread. 84 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 85 worker, "CanvasShutdownManager", []() { Shutdown(); }); 86 if (NS_WARN_IF(!workerRef)) { 87 return nullptr; 88 } 89 90 CanvasShutdownManager* manager = new CanvasShutdownManager(workerRef); 91 sLocalManager.set(manager); 92 93 StaticMutexAutoLock lock(sManagersMutex); 94 sManagers.insert(manager); 95 return manager; 96 } 97 98 if (NS_IsMainThread()) { 99 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { 100 return nullptr; 101 } 102 103 CanvasShutdownManager* manager = new CanvasShutdownManager(); 104 sLocalManager.set(manager); 105 106 StaticMutexAutoLock lock(sManagersMutex); 107 sManagers.insert(manager); 108 return manager; 109 } 110 111 MOZ_ASSERT_UNREACHABLE("Can only be used on main or DOM worker threads!"); 112 return nullptr; 113 } 114 115 void CanvasShutdownManager::AddShutdownObserver( 116 dom::CanvasRenderingContext2D* aCanvas) { 117 mActiveCanvas.insert(aCanvas); 118 } 119 120 void CanvasShutdownManager::RemoveShutdownObserver( 121 dom::CanvasRenderingContext2D* aCanvas) { 122 mActiveCanvas.erase(aCanvas); 123 } 124 125 void CanvasShutdownManager::OnRemoteCanvasLost() { 126 // Note that the canvas cannot do anything that mutates our state. It will 127 // dispatch for anything that risks re-entrancy. 128 for (const auto& canvas : mActiveCanvas) { 129 canvas->OnRemoteCanvasLost(); 130 } 131 } 132 133 void CanvasShutdownManager::OnRemoteCanvasRestored() { 134 // Note that the canvas cannot do anything that mutates our state. It will 135 // dispatch for anything that risks re-entrancy. 136 for (const auto& canvas : mActiveCanvas) { 137 canvas->OnRemoteCanvasRestored(); 138 } 139 } 140 141 void CanvasShutdownManager::OnRemoteCanvasReset( 142 const nsTArray<layers::RemoteTextureOwnerId>& aOwnerIds) { 143 if (aOwnerIds.IsEmpty()) { 144 return; 145 } 146 147 for (const auto& canvas : mActiveCanvas) { 148 auto* bufferProvider = canvas->GetBufferProvider(); 149 if (!bufferProvider) { 150 continue; 151 } 152 153 Maybe<layers::RemoteTextureOwnerId> ownerId = 154 bufferProvider->GetRemoteTextureOwnerId(); 155 if (!ownerId) { 156 continue; 157 } 158 159 if (aOwnerIds.Contains(*ownerId)) { 160 canvas->OnRemoteCanvasLost(); 161 canvas->OnRemoteCanvasRestored(); 162 } 163 } 164 } 165 166 /* static */ void CanvasShutdownManager::MaybeRestoreRemoteCanvas() { 167 // Calling Get will recreate the CanvasManagerChild, which in turn will 168 // cause us to call OnRemoteCanvasRestore upon success. 169 if (CanvasShutdownManager* manager = MaybeGet()) { 170 if (!manager->mActiveCanvas.empty()) { 171 CanvasManagerChild::Get(); 172 } 173 } 174 } 175 176 /* static */ void CanvasShutdownManager::OnCompositorManagerRestored() { 177 MOZ_ASSERT(NS_IsMainThread()); 178 179 class RestoreRunnable final : public dom::MainThreadWorkerRunnable { 180 public: 181 RestoreRunnable() 182 : MainThreadWorkerRunnable("CanvasShutdownManager::RestoreRunnable") {} 183 184 bool WorkerRun(JSContext*, WorkerPrivate*) override { 185 MaybeRestoreRemoteCanvas(); 186 return true; 187 } 188 }; 189 190 // We can restore the main thread canvases immediately. 191 MaybeRestoreRemoteCanvas(); 192 193 // And dispatch to restore any DOM worker canvases. This is safe because we 194 // remove the manager from sManagers before clearing mWorkerRef during DOM 195 // worker shutdown. 196 StaticMutexAutoLock lock(sManagersMutex); 197 for (const auto& manager : sManagers) { 198 if (manager->mWorkerRef) { 199 auto task = MakeRefPtr<RestoreRunnable>(); 200 task->Dispatch(manager->mWorkerRef->Private()); 201 } 202 } 203 } 204 205 } // namespace mozilla::gfx