Bug 1909074. Don't pass OFFSET_BY_ORIGIN to GetResultingTransformMatrix when it's...
[gecko.git] / dom / workers / WorkerThread.cpp
blob0b089bf6578b61cb4412e93c41058e7039c3cb0d
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/. */
7 #include "WorkerThread.h"
9 #include <utility>
10 #include "WorkerPrivate.h"
11 #include "WorkerRunnable.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/CycleCollectedJSContext.h"
15 #include "mozilla/EventQueue.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/MacroForEach.h"
18 #include "mozilla/NotNull.h"
19 #include "mozilla/ThreadEventQueue.h"
20 #include "mozilla/UniquePtr.h"
21 #include "mozilla/ipc/BackgroundChild.h"
22 #include "nsCOMPtr.h"
23 #include "nsDebug.h"
24 #include "nsICancelableRunnable.h"
25 #include "nsIEventTarget.h"
26 #include "nsIRunnable.h"
27 #include "nsIThreadInternal.h"
28 #include "nsString.h"
29 #include "nsThreadUtils.h"
30 #include "prthread.h"
32 static mozilla::LazyLogModule gWorkerThread("WorkerThread");
33 #ifdef LOGV
34 # undef LOGV
35 #endif
36 #define LOGV(msg) MOZ_LOG(gWorkerThread, LogLevel::Verbose, msg);
38 namespace mozilla {
40 using namespace ipc;
42 namespace dom {
44 namespace {
46 // The C stack size. We use the same stack size on all platforms for
47 // consistency.
49 // Note: Our typical equation of 256 machine words works out to 2MB on 64-bit
50 // platforms. Since that works out to the size of a VM huge page, that can
51 // sometimes lead to an OS allocating an entire huge page for the stack at once.
52 // To avoid this, we subtract the size of 2 pages, to be safe.
53 const uint32_t kWorkerStackSize = 256 * sizeof(size_t) * 1024 - 8192;
55 } // namespace
57 WorkerThreadFriendKey::WorkerThreadFriendKey() {
58 MOZ_COUNT_CTOR(WorkerThreadFriendKey);
61 WorkerThreadFriendKey::~WorkerThreadFriendKey() {
62 MOZ_COUNT_DTOR(WorkerThreadFriendKey);
65 class WorkerThread::Observer final : public nsIThreadObserver {
66 WorkerPrivate* mWorkerPrivate;
68 public:
69 explicit Observer(WorkerPrivate* aWorkerPrivate)
70 : mWorkerPrivate(aWorkerPrivate) {
71 MOZ_ASSERT(aWorkerPrivate);
72 aWorkerPrivate->AssertIsOnWorkerThread();
75 NS_DECL_THREADSAFE_ISUPPORTS
77 private:
78 ~Observer() { mWorkerPrivate->AssertIsOnWorkerThread(); }
80 NS_DECL_NSITHREADOBSERVER
83 WorkerThread::WorkerThread(ConstructorKey)
84 : nsThread(
85 MakeNotNull<ThreadEventQueue*>(MakeUnique<mozilla::EventQueue>()),
86 nsThread::NOT_MAIN_THREAD, {.stackSize = kWorkerStackSize}),
87 mLock("WorkerThread::mLock"),
88 mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar"),
89 mWorkerPrivate(nullptr),
90 mOtherThreadsDispatchingViaEventTarget(0)
91 #ifdef DEBUG
93 mAcceptingNonWorkerRunnables(true)
94 #endif
98 WorkerThread::~WorkerThread() {
99 MOZ_ASSERT(!mWorkerPrivate);
100 MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget);
101 MOZ_ASSERT(mAcceptingNonWorkerRunnables);
104 // static
105 SafeRefPtr<WorkerThread> WorkerThread::Create(
106 const WorkerThreadFriendKey& /* aKey */) {
107 SafeRefPtr<WorkerThread> thread =
108 MakeSafeRefPtr<WorkerThread>(ConstructorKey());
109 if (NS_FAILED(thread->Init("DOM Worker"_ns))) {
110 NS_WARNING("Failed to create new thread!");
111 return nullptr;
114 return thread;
117 void WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */,
118 WorkerPrivate* aWorkerPrivate) {
119 MOZ_ASSERT(PR_GetCurrentThread() == mThread);
120 MOZ_ASSERT(aWorkerPrivate);
123 MutexAutoLock lock(mLock);
125 MOZ_ASSERT(!mWorkerPrivate);
126 MOZ_ASSERT(mAcceptingNonWorkerRunnables);
128 mWorkerPrivate = aWorkerPrivate;
129 #ifdef DEBUG
130 mAcceptingNonWorkerRunnables = false;
131 #endif
134 mObserver = new Observer(aWorkerPrivate);
135 MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver));
138 void WorkerThread::ClearEventQueueAndWorker(
139 const WorkerThreadFriendKey& /* aKey */) {
140 MOZ_ASSERT(PR_GetCurrentThread() == mThread);
142 MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver));
143 mObserver = nullptr;
146 MutexAutoLock lock(mLock);
148 MOZ_ASSERT(mWorkerPrivate);
149 MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
150 // mOtherThreadsDispatchingViaEventTarget can still be non-zero here
151 // because WorkerThread::Dispatch isn't atomic so a thread initiating
152 // dispatch can have dispatched a runnable at this thread allowing us to
153 // begin shutdown before that thread gets a chance to decrement
154 // mOtherThreadsDispatchingViaEventTarget back to 0. So we need to wait
155 // for that.
156 while (mOtherThreadsDispatchingViaEventTarget) {
157 mWorkerPrivateCondVar.Wait();
159 // Need to clean up the dispatched runnables if
160 // mOtherThreadsDispatchingViaEventTarget was non-zero.
161 if (NS_HasPendingEvents(nullptr)) {
162 NS_ProcessPendingEvents(nullptr);
164 #ifdef DEBUG
165 mAcceptingNonWorkerRunnables = true;
166 #endif
167 mWorkerPrivate = nullptr;
171 nsresult WorkerThread::DispatchPrimaryRunnable(
172 const WorkerThreadFriendKey& /* aKey */,
173 already_AddRefed<nsIRunnable> aRunnable) {
174 nsCOMPtr<nsIRunnable> runnable(aRunnable);
176 #ifdef DEBUG
177 MOZ_ASSERT(PR_GetCurrentThread() != mThread);
178 MOZ_ASSERT(runnable);
180 MutexAutoLock lock(mLock);
182 MOZ_ASSERT(!mWorkerPrivate);
183 MOZ_ASSERT(mAcceptingNonWorkerRunnables);
185 #endif
187 nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
188 if (NS_WARN_IF(NS_FAILED(rv))) {
189 return rv;
192 return NS_OK;
195 nsresult WorkerThread::DispatchAnyThread(
196 const WorkerThreadFriendKey& /* aKey */,
197 RefPtr<WorkerRunnable> aWorkerRunnable) {
198 // May be called on any thread!
200 #ifdef DEBUG
202 const bool onWorkerThread = PR_GetCurrentThread() == mThread;
204 MutexAutoLock lock(mLock);
206 MOZ_ASSERT(mWorkerPrivate);
207 MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
209 if (onWorkerThread) {
210 mWorkerPrivate->AssertIsOnWorkerThread();
214 #endif
216 nsresult rv =
217 nsThread::Dispatch(aWorkerRunnable.forget(), NS_DISPATCH_NORMAL);
218 if (NS_WARN_IF(NS_FAILED(rv))) {
219 return rv;
222 // We don't need to notify the worker's condition variable here because we're
223 // being called from worker-controlled code and it will make sure to wake up
224 // the worker thread if needed.
226 return NS_OK;
229 NS_IMETHODIMP
230 WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) {
231 nsCOMPtr<nsIRunnable> runnable(aRunnable);
232 return Dispatch(runnable.forget(), aFlags);
235 NS_IMETHODIMP
236 WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
237 uint32_t aFlags) {
238 // May be called on any thread!
239 nsCOMPtr<nsIRunnable> runnable(aRunnable); // in case we exit early
241 LOGV(("WorkerThread::Dispatch [%p] runnable: %p", this, runnable.get()));
243 // Workers only support asynchronous dispatch.
244 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
245 return NS_ERROR_UNEXPECTED;
248 const bool onWorkerThread = PR_GetCurrentThread() == mThread;
250 WorkerPrivate* workerPrivate = nullptr;
251 if (onWorkerThread) {
252 // If the mWorkerPrivate has already disconnected by
253 // WorkerPrivate::ResetWorkerPrivateInWorkerThread(), there is no chance
254 // that to execute this runnable. Return NS_ERROR_UNEXPECTED here.
255 if (!mWorkerPrivate) {
256 return NS_ERROR_UNEXPECTED;
258 // No need to lock here because it is only modified on this thread.
259 mWorkerPrivate->AssertIsOnWorkerThread();
261 workerPrivate = mWorkerPrivate;
262 } else {
263 MutexAutoLock lock(mLock);
265 MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX);
267 if (mWorkerPrivate) {
268 workerPrivate = mWorkerPrivate;
270 // Incrementing this counter will make the worker thread sleep if it
271 // somehow tries to unset mWorkerPrivate while we're using it.
272 mOtherThreadsDispatchingViaEventTarget++;
276 nsresult rv;
277 rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
279 if (!onWorkerThread && workerPrivate) {
280 // We need to wake the worker thread if we're not already on the right
281 // thread and the dispatch succeeded.
282 if (NS_SUCCEEDED(rv)) {
283 MutexAutoLock workerLock(workerPrivate->mMutex);
285 workerPrivate->mCondVar.Notify();
288 // Now unset our waiting flag.
290 MutexAutoLock lock(mLock);
292 MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget);
294 if (!--mOtherThreadsDispatchingViaEventTarget) {
295 mWorkerPrivateCondVar.Notify();
300 if (NS_WARN_IF(NS_FAILED(rv))) {
301 LOGV(("WorkerThread::Dispatch [%p] failed, runnable: %p", this,
302 runnable.get()));
303 return rv;
306 return NS_OK;
309 NS_IMETHODIMP
310 WorkerThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
311 return NS_ERROR_NOT_IMPLEMENTED;
314 uint32_t WorkerThread::RecursionDepth(
315 const WorkerThreadFriendKey& /* aKey */) const {
316 MOZ_ASSERT(PR_GetCurrentThread() == mThread);
318 return mNestedEventLoopDepth;
321 NS_IMETHODIMP
322 WorkerThread::HasPendingEvents(bool* aResult) {
323 MOZ_ASSERT(aResult);
324 const bool onWorkerThread = PR_GetCurrentThread() == mThread;
325 // If is on the worker thread, call nsThread::HasPendingEvents directly.
326 if (onWorkerThread) {
327 return nsThread::HasPendingEvents(aResult);
329 // Checking if is on the parent thread, otherwise, returns unexpected error.
331 MutexAutoLock lock(mLock);
332 // return directly if the mWorkerPrivate has not yet set or had already
333 // unset
334 if (!mWorkerPrivate) {
335 *aResult = false;
336 return NS_OK;
338 if (!mWorkerPrivate->IsOnParentThread()) {
339 *aResult = false;
340 return NS_ERROR_UNEXPECTED;
343 *aResult = mEvents->HasPendingEvent();
344 return NS_OK;
347 NS_IMPL_ISUPPORTS(WorkerThread::Observer, nsIThreadObserver)
349 NS_IMETHODIMP
350 WorkerThread::Observer::OnDispatchedEvent() {
351 MOZ_CRASH("OnDispatchedEvent() should never be called!");
354 NS_IMETHODIMP
355 WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
356 bool aMayWait) {
357 mWorkerPrivate->AssertIsOnWorkerThread();
359 // If the PBackground child is not created yet, then we must permit
360 // blocking event processing to support
361 // BackgroundChild::GetOrCreateCreateForCurrentThread(). If this occurs
362 // then we are spinning on the event queue at the start of
363 // PrimaryWorkerRunnable::Run() and don't want to process the event in
364 // mWorkerPrivate yet.
365 if (aMayWait) {
366 MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
367 MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
368 return NS_OK;
371 mWorkerPrivate->OnProcessNextEvent();
372 return NS_OK;
375 NS_IMETHODIMP
376 WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
377 bool /* aEventWasProcessed */) {
378 mWorkerPrivate->AssertIsOnWorkerThread();
380 mWorkerPrivate->AfterProcessNextEvent();
381 return NS_OK;
384 } // namespace dom
385 } // namespace mozilla