WorkerRef.cpp (7388B)
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 "mozilla/dom/WorkerRef.h" 8 9 #include "WorkerPrivate.h" 10 #include "WorkerRunnable.h" 11 #include "nsDebug.h" 12 13 namespace mozilla::dom { 14 15 namespace { 16 17 // This runnable is used to release the StrongWorkerRef on the worker thread 18 // when a ThreadSafeWorkerRef is released. 19 class ReleaseRefControlRunnable final : public WorkerControlRunnable { 20 public: 21 ReleaseRefControlRunnable(WorkerPrivate* aWorkerPrivate, 22 already_AddRefed<StrongWorkerRef> aRef) 23 : WorkerControlRunnable("ReleaseRefControlRunnable"), 24 mRef(std::move(aRef)) { 25 MOZ_ASSERT(mRef); 26 } 27 28 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; } 29 30 void PostDispatch(WorkerPrivate* aWorkerPrivate, 31 bool aDispatchResult) override {} 32 33 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 34 mRef = nullptr; 35 return true; 36 } 37 38 private: 39 RefPtr<StrongWorkerRef> mRef; 40 }; 41 42 } // namespace 43 44 // ---------------------------------------------------------------------------- 45 // WorkerRef 46 47 WorkerRef::WorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName, 48 bool aIsPreventingShutdown) 49 : 50 #ifdef DEBUG 51 mDebugMutex("WorkerRef"), 52 #endif 53 mWorkerPrivate(aWorkerPrivate), 54 mName(aName), 55 mIsPreventingShutdown(aIsPreventingShutdown), 56 mHolding(false) { 57 MOZ_ASSERT(aWorkerPrivate); 58 MOZ_ASSERT(aName); 59 60 aWorkerPrivate->AssertIsOnWorkerThread(); 61 } 62 63 WorkerRef::~WorkerRef() { 64 NS_ASSERT_OWNINGTHREAD(WorkerRef); 65 ReleaseWorker(); 66 } 67 68 void WorkerRef::ReleaseWorker() { 69 if (mHolding) { 70 MOZ_ASSERT(mWorkerPrivate); 71 72 if (mIsPreventingShutdown) { 73 mWorkerPrivate->AssertIsNotPotentiallyLastGCCCRunning(); 74 } 75 mWorkerPrivate->RemoveWorkerRef(this); 76 mWorkerPrivate = nullptr; 77 78 mHolding = false; 79 } 80 } 81 82 bool WorkerRef::HoldWorker(WorkerStatus aStatus) { 83 MOZ_ASSERT(mWorkerPrivate); 84 MOZ_ASSERT(!mHolding); 85 86 if (NS_WARN_IF(!mWorkerPrivate->AddWorkerRef(this, aStatus))) { 87 return false; 88 } 89 90 mHolding = true; 91 return true; 92 } 93 94 void WorkerRef::Notify() { 95 NS_ASSERT_OWNINGTHREAD(WorkerRef); 96 97 if (!mCallback) { 98 return; 99 } 100 101 MoveOnlyFunction<void()> callback = std::move(mCallback); 102 MOZ_ASSERT(!mCallback); 103 104 callback(); 105 } 106 107 // ---------------------------------------------------------------------------- 108 // WeakWorkerRef 109 110 /* static */ 111 already_AddRefed<WeakWorkerRef> WeakWorkerRef::Create( 112 WorkerPrivate* aWorkerPrivate, MoveOnlyFunction<void()>&& aCallback) { 113 MOZ_ASSERT(aWorkerPrivate); 114 aWorkerPrivate->AssertIsOnWorkerThread(); 115 116 RefPtr<WeakWorkerRef> ref = new WeakWorkerRef(aWorkerPrivate); 117 if (!ref->HoldWorker(Canceling)) { 118 return nullptr; 119 } 120 121 ref->mCallback = std::move(aCallback); 122 123 return ref.forget(); 124 } 125 126 WeakWorkerRef::WeakWorkerRef(WorkerPrivate* aWorkerPrivate) 127 : WorkerRef(aWorkerPrivate, "WeakWorkerRef", false) {} 128 129 WeakWorkerRef::~WeakWorkerRef() = default; 130 131 void WeakWorkerRef::Notify() { 132 MOZ_ASSERT(mHolding); 133 MOZ_ASSERT(mWorkerPrivate); 134 135 // Notify could drop the last reference to this object. We must keep it alive 136 // in order to call ReleaseWorker() immediately after. 137 RefPtr<WeakWorkerRef> kungFuGrip = this; 138 139 WorkerRef::Notify(); 140 ReleaseWorker(); 141 } 142 143 WorkerPrivate* WeakWorkerRef::GetPrivate() const { 144 NS_ASSERT_OWNINGTHREAD(WeakWorkerRef); 145 return mWorkerPrivate; 146 } 147 148 WorkerPrivate* WeakWorkerRef::GetUnsafePrivate() const { 149 return mWorkerPrivate; 150 } 151 152 // ---------------------------------------------------------------------------- 153 // StrongWorkerRef 154 155 /* static */ 156 already_AddRefed<StrongWorkerRef> StrongWorkerRef::Create( 157 WorkerPrivate* const aWorkerPrivate, const char* const aName, 158 MoveOnlyFunction<void()>&& aCallback) { 159 if (RefPtr<StrongWorkerRef> ref = 160 CreateImpl(aWorkerPrivate, aName, Canceling)) { 161 ref->mCallback = std::move(aCallback); 162 return ref.forget(); 163 } 164 return nullptr; 165 } 166 167 /* static */ 168 already_AddRefed<StrongWorkerRef> StrongWorkerRef::CreateForcibly( 169 WorkerPrivate* const aWorkerPrivate, const char* const aName) { 170 return CreateImpl(aWorkerPrivate, aName, Killing); 171 } 172 173 /* static */ 174 already_AddRefed<StrongWorkerRef> StrongWorkerRef::CreateImpl( 175 WorkerPrivate* const aWorkerPrivate, const char* const aName, 176 WorkerStatus const aFailStatus) { 177 MOZ_ASSERT(aWorkerPrivate); 178 MOZ_ASSERT(aName); 179 180 RefPtr<StrongWorkerRef> ref = new StrongWorkerRef(aWorkerPrivate, aName); 181 if (!ref->HoldWorker(aFailStatus)) { 182 return nullptr; 183 } 184 185 return ref.forget(); 186 } 187 188 StrongWorkerRef::StrongWorkerRef(WorkerPrivate* aWorkerPrivate, 189 const char* aName) 190 : WorkerRef(aWorkerPrivate, aName, true) {} 191 192 StrongWorkerRef::~StrongWorkerRef() = default; 193 194 WorkerPrivate* StrongWorkerRef::Private() const { 195 NS_ASSERT_OWNINGTHREAD(StrongWorkerRef); 196 return mWorkerPrivate; 197 } 198 199 // ---------------------------------------------------------------------------- 200 // ThreadSafeWorkerRef 201 202 ThreadSafeWorkerRef::ThreadSafeWorkerRef(StrongWorkerRef* aRef) : mRef(aRef) { 203 MOZ_ASSERT(aRef); 204 aRef->Private()->AssertIsOnWorkerThread(); 205 } 206 207 ThreadSafeWorkerRef::~ThreadSafeWorkerRef() { 208 // Let's release the StrongWorkerRef on the correct thread. 209 if (!mRef->mWorkerPrivate->IsOnWorkerThread()) { 210 WorkerPrivate* workerPrivate = mRef->mWorkerPrivate; 211 RefPtr<ReleaseRefControlRunnable> r = 212 new ReleaseRefControlRunnable(workerPrivate, mRef.forget()); 213 r->Dispatch(workerPrivate); 214 return; 215 } 216 } 217 218 WorkerPrivate* ThreadSafeWorkerRef::Private() const { 219 return mRef->mWorkerPrivate; 220 } 221 222 // ---------------------------------------------------------------------------- 223 // IPCWorkerRef 224 225 /* static */ 226 already_AddRefed<IPCWorkerRef> IPCWorkerRef::Create( 227 WorkerPrivate* aWorkerPrivate, const char* aName, 228 MoveOnlyFunction<void()>&& aCallback) { 229 MOZ_ASSERT(aWorkerPrivate); 230 aWorkerPrivate->AssertIsOnWorkerThread(); 231 232 RefPtr<IPCWorkerRef> ref = new IPCWorkerRef(aWorkerPrivate, aName); 233 if (!ref->HoldWorker(Canceling)) { 234 return nullptr; 235 } 236 ref->SetActorCount(1); 237 ref->mCallback = std::move(aCallback); 238 239 return ref.forget(); 240 } 241 242 IPCWorkerRef::IPCWorkerRef(WorkerPrivate* aWorkerPrivate, const char* aName) 243 : WorkerRef(aWorkerPrivate, aName, false), mActorCount(0) {} 244 245 IPCWorkerRef::~IPCWorkerRef() { 246 NS_ASSERT_OWNINGTHREAD(IPCWorkerRef); 247 // explicit type convertion to avoid undefined behavior of uint32_t overflow. 248 mWorkerPrivate->AdjustNonblockingCCBackgroundActorCount( 249 (int32_t)-mActorCount); 250 ReleaseWorker(); 251 }; 252 253 WorkerPrivate* IPCWorkerRef::Private() const { 254 NS_ASSERT_OWNINGTHREAD(IPCWorkerRef); 255 return mWorkerPrivate; 256 } 257 258 void IPCWorkerRef::SetActorCount(uint32_t aCount) { 259 NS_ASSERT_OWNINGTHREAD(IPCWorkerRef); 260 // explicit type convertion to avoid undefined behavior of uint32_t overflow. 261 mWorkerPrivate->AdjustNonblockingCCBackgroundActorCount((int32_t)aCount - 262 (int32_t)mActorCount); 263 mActorCount = aCount; 264 } 265 266 } // namespace mozilla::dom