Backed out changeset b09d48d2b473 (bug 1655101) for causing mochitest webgl failures...
[gecko.git] / dom / workers / WorkerRunnable.cpp
blob44162dfb439892d6045c411bbd245c702bcd4bda
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 "WorkerRunnable.h"
9 #include "WorkerScope.h"
10 #include "js/RootingAPI.h"
11 #include "jsapi.h"
12 #include "jsfriendapi.h"
13 #include "mozilla/AlreadyAddRefed.h"
14 #include "mozilla/AppShutdown.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/ErrorResult.h"
18 #include "mozilla/Logging.h"
19 #include "mozilla/Maybe.h"
20 #include "mozilla/Telemetry.h"
21 #include "mozilla/TelemetryHistogramEnums.h"
22 #include "mozilla/TimeStamp.h"
23 #include "mozilla/Unused.h"
24 #include "mozilla/dom/ScriptSettings.h"
25 #include "mozilla/dom/Worker.h"
26 #include "mozilla/dom/WorkerCommon.h"
27 #include "nsDebug.h"
28 #include "nsGlobalWindowInner.h"
29 #include "nsID.h"
30 #include "nsIEventTarget.h"
31 #include "nsIGlobalObject.h"
32 #include "nsIRunnable.h"
33 #include "nsThreadUtils.h"
34 #include "nsWrapperCacheInlines.h"
36 namespace mozilla::dom {
38 static mozilla::LazyLogModule sWorkerRunnableLog("WorkerRunnable");
40 #ifdef LOG
41 # undef LOG
42 #endif
43 #define LOG(args) MOZ_LOG(sWorkerRunnableLog, LogLevel::Verbose, args);
45 namespace {
47 const nsIID kWorkerRunnableIID = {
48 0x320cc0b5,
49 0xef12,
50 0x4084,
51 {0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68}};
53 } // namespace
55 #ifdef DEBUG
56 WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
57 TargetAndBusyBehavior aBehavior)
58 : mWorkerPrivate(aWorkerPrivate),
59 mBehavior(aBehavior),
60 mCallingCancelWithinRun(false) {
61 LOG(("WorkerRunnable::WorkerRunnable [%p]", this));
62 MOZ_ASSERT(aWorkerPrivate);
64 #endif
66 bool WorkerRunnable::IsDebuggerRunnable() const { return false; }
68 nsIGlobalObject* WorkerRunnable::DefaultGlobalObject() const {
69 if (IsDebuggerRunnable()) {
70 return mWorkerPrivate->DebuggerGlobalScope();
71 } else {
72 return mWorkerPrivate->GlobalScope();
76 bool WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
77 #ifdef DEBUG
78 MOZ_ASSERT(aWorkerPrivate);
80 switch (mBehavior) {
81 case ParentThreadUnchangedBusyCount:
82 aWorkerPrivate->AssertIsOnWorkerThread();
83 break;
85 case WorkerThreadModifyBusyCount:
86 case WorkerThreadUnchangedBusyCount:
87 aWorkerPrivate->AssertIsOnParentThread();
88 break;
90 default:
91 MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
93 #endif
95 if (mBehavior == WorkerThreadModifyBusyCount) {
96 return aWorkerPrivate->ModifyBusyCount(true);
99 return true;
102 bool WorkerRunnable::Dispatch() {
103 bool ok = PreDispatch(mWorkerPrivate);
104 if (ok) {
105 ok = DispatchInternal();
107 PostDispatch(mWorkerPrivate, ok);
108 return ok;
111 bool WorkerRunnable::DispatchInternal() {
112 LOG(("WorkerRunnable::DispatchInternal [%p]", this));
113 RefPtr<WorkerRunnable> runnable(this);
115 if (mBehavior == WorkerThreadModifyBusyCount ||
116 mBehavior == WorkerThreadUnchangedBusyCount) {
117 if (IsDebuggerRunnable()) {
118 return NS_SUCCEEDED(
119 mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget()));
120 } else {
121 return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget()));
125 MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
127 if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
128 return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
131 if (IsDebuggeeRunnable()) {
132 RefPtr<WorkerDebuggeeRunnable> debuggeeRunnable =
133 runnable.forget().downcast<WorkerDebuggeeRunnable>();
134 return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggeeToMainThread(
135 debuggeeRunnable.forget(), NS_DISPATCH_NORMAL));
138 return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
141 void WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
142 bool aDispatchResult) {
143 MOZ_ASSERT(aWorkerPrivate);
145 #ifdef DEBUG
146 switch (mBehavior) {
147 case ParentThreadUnchangedBusyCount:
148 aWorkerPrivate->AssertIsOnWorkerThread();
149 break;
151 case WorkerThreadModifyBusyCount:
152 aWorkerPrivate->AssertIsOnParentThread();
153 break;
155 case WorkerThreadUnchangedBusyCount:
156 aWorkerPrivate->AssertIsOnParentThread();
157 break;
159 default:
160 MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
162 #endif
164 if (!aDispatchResult) {
165 if (mBehavior == WorkerThreadModifyBusyCount) {
166 aWorkerPrivate->ModifyBusyCount(false);
171 bool WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate) { return true; }
173 void WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
174 bool aRunResult) {
175 MOZ_ASSERT(aCx);
176 MOZ_ASSERT(aWorkerPrivate);
178 #ifdef DEBUG
179 switch (mBehavior) {
180 case ParentThreadUnchangedBusyCount:
181 aWorkerPrivate->AssertIsOnParentThread();
182 break;
184 case WorkerThreadModifyBusyCount:
185 aWorkerPrivate->AssertIsOnWorkerThread();
186 break;
188 case WorkerThreadUnchangedBusyCount:
189 aWorkerPrivate->AssertIsOnWorkerThread();
190 break;
192 default:
193 MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
195 #endif
197 if (mBehavior == WorkerThreadModifyBusyCount) {
198 aWorkerPrivate->ModifyBusyCountFromWorker(false);
202 // static
203 WorkerRunnable* WorkerRunnable::FromRunnable(nsIRunnable* aRunnable) {
204 MOZ_ASSERT(aRunnable);
206 WorkerRunnable* runnable;
207 nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
208 reinterpret_cast<void**>(&runnable));
209 if (NS_FAILED(rv)) {
210 return nullptr;
213 MOZ_ASSERT(runnable);
214 return runnable;
217 NS_IMPL_ADDREF(WorkerRunnable)
218 NS_IMPL_RELEASE(WorkerRunnable)
220 NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
221 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
222 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
223 // kWorkerRunnableIID is special in that it does not AddRef its result.
224 if (aIID.Equals(kWorkerRunnableIID)) {
225 *aInstancePtr = this;
226 return NS_OK;
227 } else
228 NS_INTERFACE_MAP_END
230 NS_IMETHODIMP
231 WorkerRunnable::Run() {
232 LOG(("WorkerRunnable::Run [%p]", this));
233 bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
234 mBehavior == WorkerThreadUnchangedBusyCount;
236 #ifdef DEBUG
237 if (targetIsWorkerThread) {
238 mWorkerPrivate->AssertIsOnWorkerThread();
239 } else {
240 MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
241 mWorkerPrivate->AssertIsOnParentThread();
243 #endif
245 if (targetIsWorkerThread && !mCallingCancelWithinRun &&
246 mWorkerPrivate->CancelBeforeWorkerScopeConstructed()) {
247 mCallingCancelWithinRun = true;
248 Cancel();
249 mCallingCancelWithinRun = false;
250 if (mBehavior == WorkerThreadModifyBusyCount) {
251 mWorkerPrivate->ModifyBusyCountFromWorker(false);
253 return NS_OK;
256 bool result = PreRun(mWorkerPrivate);
257 if (!result) {
258 MOZ_ASSERT(targetIsWorkerThread,
259 "The only PreRun implementation that can fail is "
260 "ScriptExecutorRunnable");
261 mWorkerPrivate->AssertIsOnWorkerThread();
262 MOZ_ASSERT(!JS_IsExceptionPending(mWorkerPrivate->GetJSContext()));
263 // We can't enter a useful realm on the JSContext here; just pass it
264 // in as-is.
265 PostRun(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false);
266 return NS_ERROR_FAILURE;
269 // Track down the appropriate global, if any, to use for the AutoEntryScript.
270 nsCOMPtr<nsIGlobalObject> globalObject;
271 bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
272 MOZ_ASSERT(isMainThread == NS_IsMainThread());
273 RefPtr<WorkerPrivate> kungFuDeathGrip;
274 if (targetIsWorkerThread) {
275 globalObject = mWorkerPrivate->GetCurrentEventLoopGlobal();
276 if (!globalObject) {
277 globalObject = DefaultGlobalObject();
278 // Our worker thread may not be in a good state here if there is no
279 // JSContext avaliable. The way this manifests itself is that
280 // globalObject ends up null (though it's not clear to me how we can be
281 // running runnables at all when DefaultGlobalObject() is returning
282 // false!) and then when we try to init the AutoJSAPI either
283 // CycleCollectedJSContext::Get() returns null or it has a null JSContext.
284 // In any case, we used to have a check for
285 // GetCurrentWorkerThreadJSContext() being non-null here and that seems to
286 // avoid the problem, so let's keep doing that check even if we don't need
287 // the JSContext here at all.
288 if (NS_WARN_IF(!globalObject && !GetCurrentWorkerThreadJSContext())) {
289 return NS_ERROR_FAILURE;
293 // We may still not have a globalObject here: in the case of
294 // CompileScriptRunnable, we don't actually create the global object until
295 // we have the script data, which happens in a syncloop under
296 // CompileScriptRunnable::WorkerRun, so we can't assert that it got created
297 // in the PreRun call above.
298 } else {
299 kungFuDeathGrip = mWorkerPrivate;
300 if (isMainThread) {
301 globalObject = nsGlobalWindowInner::Cast(mWorkerPrivate->GetWindow());
302 } else {
303 globalObject = mWorkerPrivate->GetParent()->GlobalScope();
307 // We might run script as part of WorkerRun, so we need an AutoEntryScript.
308 // This is part of the HTML spec for workers at:
309 // http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker
310 // If we don't have a globalObject we have to use an AutoJSAPI instead, but
311 // this is OK as we won't be running script in these circumstances.
312 Maybe<mozilla::dom::AutoJSAPI> maybeJSAPI;
313 Maybe<mozilla::dom::AutoEntryScript> aes;
314 JSContext* cx;
315 AutoJSAPI* jsapi;
316 if (globalObject) {
317 aes.emplace(globalObject, "Worker runnable", isMainThread);
318 jsapi = aes.ptr();
319 cx = aes->cx();
320 } else {
321 maybeJSAPI.emplace();
322 maybeJSAPI->Init();
323 jsapi = maybeJSAPI.ptr();
324 cx = jsapi->cx();
327 // Note that we can't assert anything about
328 // mWorkerPrivate->ParentEventTargetRef()->GetWrapper()
329 // existing, since it may in fact have been GCed (and we may be one of the
330 // runnables cleaning up the worker as a result).
332 // If we are on the parent thread and that thread is not the main thread,
333 // then we must be a dedicated worker (because there are no
334 // Shared/ServiceWorkers whose parent is itself a worker) and then we
335 // definitely have a globalObject. If it _is_ the main thread, globalObject
336 // can be null for workers started from JSMs or other non-window contexts,
337 // sadly.
338 MOZ_ASSERT_IF(!targetIsWorkerThread && !isMainThread,
339 mWorkerPrivate->IsDedicatedWorker() && globalObject);
341 // If we're on the parent thread we might be in a null realm in the
342 // situation described above when globalObject is null. Make sure to enter
343 // the realm of the worker's reflector if there is one. There might
344 // not be one if we're just starting to compile the script for this worker.
345 Maybe<JSAutoRealm> ar;
346 if (!targetIsWorkerThread && mWorkerPrivate->IsDedicatedWorker() &&
347 mWorkerPrivate->ParentEventTargetRef()->GetWrapper()) {
348 JSObject* wrapper = mWorkerPrivate->ParentEventTargetRef()->GetWrapper();
350 // If we're on the parent thread and have a reflector and a globalObject,
351 // then the realms of cx, globalObject, and the worker's reflector
352 // should all match.
353 MOZ_ASSERT_IF(globalObject,
354 js::GetNonCCWObjectRealm(wrapper) == js::GetContextRealm(cx));
355 MOZ_ASSERT_IF(globalObject,
356 js::GetNonCCWObjectRealm(wrapper) ==
357 js::GetNonCCWObjectRealm(
358 globalObject->GetGlobalJSObjectPreserveColor()));
360 // If we're on the parent thread and have a reflector, then our
361 // JSContext had better be either in the null realm (and hence
362 // have no globalObject) or in the realm of our reflector.
363 MOZ_ASSERT(!js::GetContextRealm(cx) ||
364 js::GetNonCCWObjectRealm(wrapper) == js::GetContextRealm(cx),
365 "Must either be in the null compartment or in our reflector "
366 "compartment");
368 ar.emplace(cx, wrapper);
371 MOZ_ASSERT(!jsapi->HasException());
372 result = WorkerRun(cx, mWorkerPrivate);
373 jsapi->ReportException();
375 // We can't even assert that this didn't create our global, since in the case
376 // of CompileScriptRunnable it _does_.
378 // It would be nice to avoid passing a JSContext to PostRun, but in the case
379 // of ScriptExecutorRunnable we need to know the current compartment on the
380 // JSContext (the one we set up based on the global returned from PreRun) so
381 // that we can sanely do exception reporting. In particular, we want to make
382 // sure that we do our JS_SetPendingException while still in that compartment,
383 // because otherwise we might end up trying to create a cross-compartment
384 // wrapper when we try to move the JS exception from our runnable's
385 // ErrorResult to the JSContext, and that's not desirable in this case.
387 // We _could_ skip passing a JSContext here and then in
388 // ScriptExecutorRunnable::PostRun end up grabbing it from the WorkerPrivate
389 // and looking at its current compartment. But that seems like slightly weird
390 // action-at-a-distance...
392 // In any case, we do NOT try to change the compartment on the JSContext at
393 // this point; in the one case in which we could do that
394 // (CompileScriptRunnable) it actually doesn't matter which compartment we're
395 // in for PostRun.
396 PostRun(cx, mWorkerPrivate, result);
397 MOZ_ASSERT(!jsapi->HasException());
399 return result ? NS_OK : NS_ERROR_FAILURE;
402 nsresult WorkerRunnable::Cancel() {
403 LOG(("WorkerRunnable::Cancel [%p]", this));
404 return NS_OK;
407 void WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
408 bool aDispatchResult) {}
410 WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
411 nsIEventTarget* aSyncLoopTarget)
412 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
413 mSyncLoopTarget(aSyncLoopTarget) {
414 #ifdef DEBUG
415 if (mSyncLoopTarget) {
416 mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
418 #endif
421 WorkerSyncRunnable::WorkerSyncRunnable(
422 WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget)
423 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
424 mSyncLoopTarget(std::move(aSyncLoopTarget)) {
425 #ifdef DEBUG
426 if (mSyncLoopTarget) {
427 mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
429 #endif
432 WorkerSyncRunnable::~WorkerSyncRunnable() = default;
434 bool WorkerSyncRunnable::DispatchInternal() {
435 if (mSyncLoopTarget) {
436 RefPtr<WorkerSyncRunnable> runnable(this);
437 return NS_SUCCEEDED(
438 mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
441 return WorkerRunnable::DispatchInternal();
444 void MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
445 bool aDispatchResult) {}
447 MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable(
448 WorkerPrivate* aWorkerPrivate, nsCOMPtr<nsIEventTarget>&& aSyncLoopTarget,
449 nsresult aResult)
450 : WorkerSyncRunnable(aWorkerPrivate, std::move(aSyncLoopTarget)),
451 mResult(aResult) {
452 LOG(("MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable [%p]",
453 this));
455 AssertIsOnMainThread();
456 #ifdef DEBUG
457 mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
458 #endif
461 nsresult MainThreadStopSyncLoopRunnable::Cancel() {
462 LOG(("MainThreadStopSyncLoopRunnable::Cancel [%p]", this));
463 nsresult rv = Run();
464 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Run() failed");
466 return rv;
469 bool MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
470 WorkerPrivate* aWorkerPrivate) {
471 aWorkerPrivate->AssertIsOnWorkerThread();
472 MOZ_ASSERT(mSyncLoopTarget);
474 nsCOMPtr<nsIEventTarget> syncLoopTarget;
475 mSyncLoopTarget.swap(syncLoopTarget);
477 aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
478 return true;
481 bool MainThreadStopSyncLoopRunnable::DispatchInternal() {
482 MOZ_ASSERT(mSyncLoopTarget);
484 RefPtr<MainThreadStopSyncLoopRunnable> runnable(this);
485 return NS_SUCCEEDED(
486 mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
489 void MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
490 bool aDispatchResult) {}
492 #ifdef DEBUG
493 WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
494 TargetAndBusyBehavior aBehavior)
495 : WorkerRunnable(aWorkerPrivate, aBehavior) {
496 MOZ_ASSERT(aWorkerPrivate);
497 MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
498 aBehavior == WorkerThreadUnchangedBusyCount,
499 "WorkerControlRunnables should not modify the busy count");
501 #endif
503 nsresult WorkerControlRunnable::Cancel() {
504 LOG(("WorkerControlRunnable::Cancel [%p]", this));
505 if (NS_FAILED(Run())) {
506 NS_WARNING("WorkerControlRunnable::Run() failed.");
509 return NS_OK;
512 bool WorkerControlRunnable::DispatchInternal() {
513 RefPtr<WorkerControlRunnable> runnable(this);
515 if (mBehavior == WorkerThreadUnchangedBusyCount) {
516 return NS_SUCCEEDED(
517 mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
520 if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
521 return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
524 return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
527 WorkerMainThreadRunnable::WorkerMainThreadRunnable(
528 WorkerPrivate* aWorkerPrivate, const nsACString& aTelemetryKey)
529 : mozilla::Runnable("dom::WorkerMainThreadRunnable"),
530 mWorkerPrivate(aWorkerPrivate),
531 mTelemetryKey(aTelemetryKey) {
532 mWorkerPrivate->AssertIsOnWorkerThread();
535 WorkerMainThreadRunnable::~WorkerMainThreadRunnable() = default;
537 void WorkerMainThreadRunnable::Dispatch(WorkerStatus aFailStatus,
538 mozilla::ErrorResult& aRv) {
539 mWorkerPrivate->AssertIsOnWorkerThread();
541 TimeStamp startTime = TimeStamp::NowLoRes();
543 AutoSyncLoopHolder syncLoop(mWorkerPrivate, aFailStatus);
545 mSyncLoopTarget = syncLoop.GetSerialEventTarget();
546 if (!mSyncLoopTarget) {
547 // SyncLoop creation can fail if the worker is shutting down.
548 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
549 return;
552 DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
553 MOZ_ASSERT(
554 NS_SUCCEEDED(rv),
555 "Should only fail after xpcom-shutdown-threads and we're gone by then");
557 bool success = NS_SUCCEEDED(syncLoop.Run());
559 Telemetry::Accumulate(
560 Telemetry::SYNC_WORKER_OPERATION, mTelemetryKey,
561 static_cast<uint32_t>(
562 (TimeStamp::NowLoRes() - startTime).ToMilliseconds()));
564 Unused << startTime; // Shut the compiler up.
566 if (!success) {
567 aRv.ThrowUncatchableException();
571 NS_IMETHODIMP
572 WorkerMainThreadRunnable::Run() {
573 AssertIsOnMainThread();
575 // This shouldn't be necessary once we're better about making sure no workers
576 // are created during shutdown in earlier phases.
577 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
578 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
581 bool runResult = MainThreadRun();
583 RefPtr<MainThreadStopSyncLoopRunnable> response =
584 new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
585 std::move(mSyncLoopTarget),
586 runResult ? NS_OK : NS_ERROR_FAILURE);
588 MOZ_ALWAYS_TRUE(response->Dispatch());
590 return NS_OK;
593 bool WorkerSameThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
594 // We don't call WorkerRunnable::PreDispatch, because we're using
595 // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
596 // that PreDispatch is on the parent thread in that case.
597 aWorkerPrivate->AssertIsOnWorkerThread();
598 return true;
601 void WorkerSameThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
602 bool aDispatchResult) {
603 // We don't call WorkerRunnable::PostDispatch, because we're using
604 // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
605 // that PostDispatch is on the parent thread in that case.
606 aWorkerPrivate->AssertIsOnWorkerThread();
607 if (aDispatchResult) {
608 DebugOnly<bool> willIncrement =
609 aWorkerPrivate->ModifyBusyCountFromWorker(true);
610 // Should never fail since if this thread is still running, so should the
611 // parent and it should be able to process a control runnable.
612 MOZ_ASSERT(willIncrement);
616 WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable()
617 : mozilla::Runnable("dom::WorkerProxyToMainThreadRunnable") {}
619 WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable() = default;
621 bool WorkerProxyToMainThreadRunnable::Dispatch(WorkerPrivate* aWorkerPrivate) {
622 MOZ_ASSERT(aWorkerPrivate);
623 aWorkerPrivate->AssertIsOnWorkerThread();
625 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
626 aWorkerPrivate, "WorkerProxyToMainThreadRunnable");
627 if (NS_WARN_IF(!workerRef)) {
628 RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
629 return false;
632 MOZ_ASSERT(!mWorkerRef);
633 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
635 if (ForMessaging()
636 ? NS_WARN_IF(NS_FAILED(
637 aWorkerPrivate->DispatchToMainThreadForMessaging(this)))
638 : NS_WARN_IF(NS_FAILED(aWorkerPrivate->DispatchToMainThread(this)))) {
639 ReleaseWorker();
640 RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
641 return false;
644 return true;
647 NS_IMETHODIMP
648 WorkerProxyToMainThreadRunnable::Run() {
649 AssertIsOnMainThread();
650 RunOnMainThread(mWorkerRef->Private());
651 PostDispatchOnMainThread();
652 return NS_OK;
655 void WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread() {
656 class ReleaseRunnable final : public MainThreadWorkerControlRunnable {
657 RefPtr<WorkerProxyToMainThreadRunnable> mRunnable;
659 public:
660 ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
661 WorkerProxyToMainThreadRunnable* aRunnable)
662 : MainThreadWorkerControlRunnable(aWorkerPrivate),
663 mRunnable(aRunnable) {
664 MOZ_ASSERT(aRunnable);
667 virtual nsresult Cancel() override {
668 Unused << WorkerRun(nullptr, mWorkerPrivate);
669 return NS_OK;
672 virtual bool WorkerRun(JSContext* aCx,
673 WorkerPrivate* aWorkerPrivate) override {
674 MOZ_ASSERT(aWorkerPrivate);
675 aWorkerPrivate->AssertIsOnWorkerThread();
677 if (mRunnable) {
678 mRunnable->RunBackOnWorkerThreadForCleanup(aWorkerPrivate);
680 // Let's release the worker thread.
681 mRunnable->ReleaseWorker();
682 mRunnable = nullptr;
685 return true;
688 private:
689 ~ReleaseRunnable() = default;
692 RefPtr<WorkerControlRunnable> runnable =
693 new ReleaseRunnable(mWorkerRef->Private(), this);
694 Unused << NS_WARN_IF(!runnable->Dispatch());
697 void WorkerProxyToMainThreadRunnable::ReleaseWorker() { mWorkerRef = nullptr; }
699 bool WorkerDebuggeeRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate) {
700 if (mBehavior == ParentThreadUnchangedBusyCount) {
701 RefPtr<StrongWorkerRef> strongRef = StrongWorkerRef::Create(
702 aWorkerPrivate, "WorkerDebuggeeRunnable::mSender");
703 if (!strongRef) {
704 return false;
707 mSender = new ThreadSafeWorkerRef(strongRef);
710 return WorkerRunnable::PreDispatch(aWorkerPrivate);
713 } // namespace mozilla::dom