Line data Source code
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 "WorkerRunnable.h"
8 :
9 : #include "nsGlobalWindow.h"
10 : #include "nsIEventTarget.h"
11 : #include "nsIGlobalObject.h"
12 : #include "nsIRunnable.h"
13 : #include "nsThreadUtils.h"
14 :
15 : #include "mozilla/DebugOnly.h"
16 : #include "mozilla/ErrorResult.h"
17 : #include "mozilla/dom/ScriptSettings.h"
18 : #include "mozilla/Telemetry.h"
19 :
20 : #include "js/RootingAPI.h"
21 : #include "js/Value.h"
22 :
23 : #include "WorkerPrivate.h"
24 : #include "WorkerScope.h"
25 :
26 : USING_WORKERS_NAMESPACE
27 :
28 : namespace {
29 :
30 : const nsIID kWorkerRunnableIID = {
31 : 0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
32 : };
33 :
34 : } // namespace
35 :
36 : #ifdef DEBUG
37 39 : WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
38 39 : TargetAndBusyBehavior aBehavior)
39 : : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
40 39 : mCallingCancelWithinRun(false)
41 : {
42 39 : MOZ_ASSERT(aWorkerPrivate);
43 39 : }
44 : #endif
45 :
46 : bool
47 3 : WorkerRunnable::IsDebuggerRunnable() const
48 : {
49 3 : return false;
50 : }
51 :
52 : nsIGlobalObject*
53 2 : WorkerRunnable::DefaultGlobalObject() const
54 : {
55 2 : if (IsDebuggerRunnable()) {
56 0 : return mWorkerPrivate->DebuggerGlobalScope();
57 : } else {
58 2 : return mWorkerPrivate->GlobalScope();
59 : }
60 : }
61 :
62 : bool
63 3 : WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
64 : {
65 : #ifdef DEBUG
66 3 : MOZ_ASSERT(aWorkerPrivate);
67 :
68 3 : switch (mBehavior) {
69 : case ParentThreadUnchangedBusyCount:
70 1 : aWorkerPrivate->AssertIsOnWorkerThread();
71 1 : break;
72 :
73 : case WorkerThreadModifyBusyCount:
74 : case WorkerThreadUnchangedBusyCount:
75 2 : aWorkerPrivate->AssertIsOnParentThread();
76 2 : break;
77 :
78 : default:
79 0 : MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
80 : }
81 : #endif
82 :
83 3 : if (mBehavior == WorkerThreadModifyBusyCount) {
84 2 : return aWorkerPrivate->ModifyBusyCount(true);
85 : }
86 :
87 1 : return true;
88 : }
89 :
90 : bool
91 39 : WorkerRunnable::Dispatch()
92 : {
93 39 : bool ok = PreDispatch(mWorkerPrivate);
94 39 : if (ok) {
95 39 : ok = DispatchInternal();
96 : }
97 39 : PostDispatch(mWorkerPrivate, ok);
98 39 : return ok;
99 : }
100 :
101 : bool
102 2 : WorkerRunnable::DispatchInternal()
103 : {
104 4 : RefPtr<WorkerRunnable> runnable(this);
105 :
106 2 : if (mBehavior == WorkerThreadModifyBusyCount ||
107 0 : mBehavior == WorkerThreadUnchangedBusyCount) {
108 2 : if (IsDebuggerRunnable()) {
109 0 : return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget()));
110 : } else {
111 2 : return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget()));
112 : }
113 : }
114 :
115 0 : MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
116 :
117 0 : if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
118 0 : return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
119 : }
120 :
121 0 : return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
122 : }
123 :
124 : void
125 3 : WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
126 : bool aDispatchResult)
127 : {
128 3 : MOZ_ASSERT(aWorkerPrivate);
129 :
130 : #ifdef DEBUG
131 3 : switch (mBehavior) {
132 : case ParentThreadUnchangedBusyCount:
133 1 : aWorkerPrivate->AssertIsOnWorkerThread();
134 1 : break;
135 :
136 : case WorkerThreadModifyBusyCount:
137 2 : aWorkerPrivate->AssertIsOnParentThread();
138 2 : break;
139 :
140 : case WorkerThreadUnchangedBusyCount:
141 0 : aWorkerPrivate->AssertIsOnParentThread();
142 0 : break;
143 :
144 : default:
145 0 : MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
146 : }
147 : #endif
148 :
149 3 : if (!aDispatchResult) {
150 0 : if (mBehavior == WorkerThreadModifyBusyCount) {
151 0 : aWorkerPrivate->ModifyBusyCount(false);
152 : }
153 : }
154 3 : }
155 :
156 : bool
157 30 : WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate)
158 : {
159 30 : return true;
160 : }
161 :
162 : void
163 29 : WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
164 : bool aRunResult)
165 : {
166 29 : MOZ_ASSERT(aCx);
167 29 : MOZ_ASSERT(aWorkerPrivate);
168 :
169 : #ifdef DEBUG
170 29 : switch (mBehavior) {
171 : case ParentThreadUnchangedBusyCount:
172 1 : aWorkerPrivate->AssertIsOnParentThread();
173 1 : break;
174 :
175 : case WorkerThreadModifyBusyCount:
176 0 : aWorkerPrivate->AssertIsOnWorkerThread();
177 0 : break;
178 :
179 : case WorkerThreadUnchangedBusyCount:
180 28 : aWorkerPrivate->AssertIsOnWorkerThread();
181 28 : break;
182 :
183 : default:
184 0 : MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
185 : }
186 : #endif
187 :
188 29 : if (mBehavior == WorkerThreadModifyBusyCount) {
189 0 : aWorkerPrivate->ModifyBusyCountFromWorker(false);
190 : }
191 29 : }
192 :
193 : // static
194 : WorkerRunnable*
195 36 : WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
196 : {
197 36 : MOZ_ASSERT(aRunnable);
198 :
199 : WorkerRunnable* runnable;
200 72 : nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
201 72 : reinterpret_cast<void**>(&runnable));
202 36 : if (NS_FAILED(rv)) {
203 0 : return nullptr;
204 : }
205 :
206 36 : MOZ_ASSERT(runnable);
207 36 : return runnable;
208 : }
209 :
210 229 : NS_IMPL_ADDREF(WorkerRunnable)
211 222 : NS_IMPL_RELEASE(WorkerRunnable)
212 :
213 188 : NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
214 188 : NS_INTERFACE_MAP_ENTRY(nsIRunnable)
215 76 : NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
216 76 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
217 : // kWorkerRunnableIID is special in that it does not AddRef its result.
218 76 : if (aIID.Equals(kWorkerRunnableIID)) {
219 36 : *aInstancePtr = this;
220 36 : return NS_OK;
221 : }
222 : else
223 40 : NS_INTERFACE_MAP_END
224 :
225 : NS_IMETHODIMP
226 36 : WorkerRunnable::Run()
227 : {
228 71 : bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
229 71 : mBehavior == WorkerThreadUnchangedBusyCount;
230 :
231 : #ifdef DEBUG
232 36 : MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
233 36 : if (targetIsWorkerThread) {
234 35 : mWorkerPrivate->AssertIsOnWorkerThread();
235 : }
236 : else {
237 1 : MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
238 1 : mWorkerPrivate->AssertIsOnParentThread();
239 : }
240 : #endif
241 :
242 36 : if (IsCanceled() && !mCallingCancelWithinRun) {
243 0 : return NS_OK;
244 : }
245 :
246 71 : if (targetIsWorkerThread &&
247 35 : mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
248 36 : !IsCanceled() && !mCallingCancelWithinRun) {
249 :
250 : // Prevent recursion.
251 0 : mCallingCancelWithinRun = true;
252 :
253 0 : Cancel();
254 :
255 0 : MOZ_ASSERT(mCallingCancelWithinRun);
256 0 : mCallingCancelWithinRun = false;
257 :
258 0 : MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
259 :
260 0 : if (mBehavior == WorkerThreadModifyBusyCount) {
261 0 : mWorkerPrivate->ModifyBusyCountFromWorker(false);
262 : }
263 :
264 0 : return NS_OK;
265 : }
266 :
267 36 : bool result = PreRun(mWorkerPrivate);
268 36 : if (!result) {
269 0 : MOZ_ASSERT(targetIsWorkerThread,
270 : "The only PreRun implementation that can fail is "
271 : "ScriptExecutorRunnable");
272 0 : mWorkerPrivate->AssertIsOnWorkerThread();
273 0 : MOZ_ASSERT(!JS_IsExceptionPending(mWorkerPrivate->GetJSContext()));
274 : // We can't enter a useful compartment on the JSContext here; just pass it
275 : // in as-is.
276 0 : PostRun(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false);
277 0 : return NS_ERROR_FAILURE;
278 : }
279 :
280 : // Track down the appropriate global, if any, to use for the AutoEntryScript.
281 68 : nsCOMPtr<nsIGlobalObject> globalObject;
282 36 : bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
283 36 : MOZ_ASSERT(isMainThread == NS_IsMainThread());
284 68 : RefPtr<WorkerPrivate> kungFuDeathGrip;
285 36 : if (targetIsWorkerThread) {
286 35 : JSContext* cx = GetCurrentThreadJSContext();
287 35 : if (NS_WARN_IF(!cx)) {
288 0 : return NS_ERROR_FAILURE;
289 : }
290 :
291 35 : JSObject* global = JS::CurrentGlobalOrNull(cx);
292 35 : if (global) {
293 33 : globalObject = xpc::NativeGlobal(global);
294 : } else {
295 2 : globalObject = DefaultGlobalObject();
296 : }
297 :
298 : // We may still not have a globalObject here: in the case of
299 : // CompileScriptRunnable, we don't actually create the global object until
300 : // we have the script data, which happens in a syncloop under
301 : // CompileScriptRunnable::WorkerRun, so we can't assert that it got created
302 : // in the PreRun call above.
303 : } else {
304 1 : kungFuDeathGrip = mWorkerPrivate;
305 1 : if (isMainThread) {
306 1 : globalObject = nsGlobalWindow::Cast(mWorkerPrivate->GetWindow());
307 : } else {
308 0 : globalObject = mWorkerPrivate->GetParent()->GlobalScope();
309 : }
310 : }
311 :
312 : // We might run script as part of WorkerRun, so we need an AutoEntryScript.
313 : // This is part of the HTML spec for workers at:
314 : // http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker
315 : // If we don't have a globalObject we have to use an AutoJSAPI instead, but
316 : // this is OK as we won't be running script in these circumstances.
317 68 : Maybe<mozilla::dom::AutoJSAPI> maybeJSAPI;
318 68 : Maybe<mozilla::dom::AutoEntryScript> aes;
319 : JSContext* cx;
320 : AutoJSAPI* jsapi;
321 36 : if (globalObject) {
322 34 : aes.emplace(globalObject, "Worker runnable", isMainThread);
323 34 : jsapi = aes.ptr();
324 34 : cx = aes->cx();
325 : } else {
326 2 : maybeJSAPI.emplace();
327 2 : maybeJSAPI->Init();
328 2 : jsapi = maybeJSAPI.ptr();
329 2 : cx = jsapi->cx();
330 : }
331 :
332 : // Note that we can't assert anything about mWorkerPrivate->GetWrapper()
333 : // existing, since it may in fact have been GCed (and we may be one of the
334 : // runnables cleaning up the worker as a result).
335 :
336 : // If we are on the parent thread and that thread is not the main thread,
337 : // then we must be a dedicated worker (because there are no
338 : // Shared/ServiceWorkers whose parent is itself a worker) and then we
339 : // definitely have a globalObject. If it _is_ the main thread, globalObject
340 : // can be null for workers started from JSMs or other non-window contexts,
341 : // sadly.
342 36 : MOZ_ASSERT_IF(!targetIsWorkerThread && !isMainThread,
343 : mWorkerPrivate->IsDedicatedWorker() && globalObject);
344 :
345 : // If we're on the parent thread we might be in a null compartment in the
346 : // situation described above when globalObject is null. Make sure to enter
347 : // the compartment of the worker's reflector if there is one. There might
348 : // not be one if we're just starting to compile the script for this worker.
349 68 : Maybe<JSAutoCompartment> ac;
350 36 : if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
351 : // If we're on the parent thread and have a reflector and a globalObject,
352 : // then the compartments of cx, globalObject, and the worker's reflector
353 : // should all match.
354 1 : MOZ_ASSERT_IF(globalObject,
355 : js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
356 : js::GetContextCompartment(cx));
357 1 : MOZ_ASSERT_IF(globalObject,
358 : js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
359 : js::GetObjectCompartment(globalObject->GetGlobalJSObject()));
360 :
361 : // If we're on the parent thread and have a reflector, then our
362 : // JSContext had better be either in the null compartment (and hence
363 : // have no globalObject) or in the compartment of our reflector.
364 1 : MOZ_ASSERT(!js::GetContextCompartment(cx) ||
365 : js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
366 : js::GetContextCompartment(cx),
367 : "Must either be in the null compartment or in our reflector "
368 : "compartment");
369 :
370 1 : ac.emplace(cx, mWorkerPrivate->GetWrapper());
371 : }
372 :
373 36 : MOZ_ASSERT(!jsapi->HasException());
374 36 : result = WorkerRun(cx, mWorkerPrivate);
375 32 : MOZ_ASSERT_IF(result, !jsapi->HasException());
376 32 : jsapi->ReportException();
377 :
378 : // We can't even assert that this didn't create our global, since in the case
379 : // of CompileScriptRunnable it _does_.
380 :
381 : // It would be nice to avoid passing a JSContext to PostRun, but in the case
382 : // of ScriptExecutorRunnable we need to know the current compartment on the
383 : // JSContext (the one we set up based on the global returned from PreRun) so
384 : // that we can sanely do exception reporting. In particular, we want to make
385 : // sure that we do our JS_SetPendingException while still in that compartment,
386 : // because otherwise we might end up trying to create a cross-compartment
387 : // wrapper when we try to move the JS exception from our runnable's
388 : // ErrorResult to the JSContext, and that's not desirable in this case.
389 : //
390 : // We _could_ skip passing a JSContext here and then in
391 : // ScriptExecutorRunnable::PostRun end up grabbing it from the WorkerPrivate
392 : // and looking at its current compartment. But that seems like slightly weird
393 : // action-at-a-distance...
394 : //
395 : // In any case, we do NOT try to change the compartment on the JSContext at
396 : // this point; in the one case in which we could do that
397 : // (CompileScriptRunnable) it actually doesn't matter which compartment we're
398 : // in for PostRun.
399 32 : PostRun(cx, mWorkerPrivate, result);
400 32 : MOZ_ASSERT(!jsapi->HasException());
401 :
402 32 : return result ? NS_OK : NS_ERROR_FAILURE;
403 : }
404 :
405 : nsresult
406 0 : WorkerRunnable::Cancel()
407 : {
408 0 : uint32_t canceledCount = ++mCanceled;
409 :
410 0 : MOZ_ASSERT(canceledCount, "Cancel() overflow!");
411 :
412 : // The docs say that Cancel() should not be called more than once and that we
413 : // should throw NS_ERROR_UNEXPECTED if it is.
414 0 : return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
415 : }
416 :
417 : void
418 0 : WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
419 : bool aDispatchResult)
420 : {
421 0 : }
422 :
423 8 : WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
424 8 : nsIEventTarget* aSyncLoopTarget)
425 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
426 8 : mSyncLoopTarget(aSyncLoopTarget)
427 : {
428 : #ifdef DEBUG
429 8 : if (mSyncLoopTarget) {
430 8 : mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
431 : }
432 : #endif
433 8 : }
434 :
435 28 : WorkerSyncRunnable::WorkerSyncRunnable(
436 : WorkerPrivate* aWorkerPrivate,
437 28 : already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
438 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
439 28 : mSyncLoopTarget(aSyncLoopTarget)
440 : {
441 : #ifdef DEBUG
442 28 : if (mSyncLoopTarget) {
443 28 : mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
444 : }
445 : #endif
446 28 : }
447 :
448 31 : WorkerSyncRunnable::~WorkerSyncRunnable()
449 : {
450 31 : }
451 :
452 : bool
453 27 : WorkerSyncRunnable::DispatchInternal()
454 : {
455 27 : if (mSyncLoopTarget) {
456 54 : RefPtr<WorkerSyncRunnable> runnable(this);
457 27 : return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
458 : }
459 :
460 0 : return WorkerRunnable::DispatchInternal();
461 : }
462 :
463 : void
464 27 : MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
465 : bool aDispatchResult)
466 : {
467 27 : }
468 :
469 9 : MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable(
470 : WorkerPrivate* aWorkerPrivate,
471 : already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
472 9 : bool aResult)
473 9 : : WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
474 : {
475 9 : AssertIsOnMainThread();
476 : #ifdef DEBUG
477 9 : mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
478 : #endif
479 9 : }
480 :
481 : nsresult
482 0 : MainThreadStopSyncLoopRunnable::Cancel()
483 : {
484 0 : nsresult rv = Run();
485 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Run() failed");
486 :
487 0 : nsresult rv2 = WorkerSyncRunnable::Cancel();
488 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "Cancel() failed");
489 :
490 0 : return NS_FAILED(rv) ? rv : rv2;
491 : }
492 :
493 : bool
494 9 : MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
495 : WorkerPrivate* aWorkerPrivate)
496 : {
497 9 : aWorkerPrivate->AssertIsOnWorkerThread();
498 9 : MOZ_ASSERT(mSyncLoopTarget);
499 :
500 18 : nsCOMPtr<nsIEventTarget> syncLoopTarget;
501 9 : mSyncLoopTarget.swap(syncLoopTarget);
502 :
503 9 : aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
504 18 : return true;
505 : }
506 :
507 : bool
508 9 : MainThreadStopSyncLoopRunnable::DispatchInternal()
509 : {
510 9 : MOZ_ASSERT(mSyncLoopTarget);
511 :
512 18 : RefPtr<MainThreadStopSyncLoopRunnable> runnable(this);
513 18 : return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
514 : }
515 :
516 : void
517 9 : MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
518 : bool aDispatchResult)
519 : {
520 9 : }
521 :
522 : #ifdef DEBUG
523 1 : WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
524 1 : TargetAndBusyBehavior aBehavior)
525 1 : : WorkerRunnable(aWorkerPrivate, aBehavior)
526 : {
527 1 : MOZ_ASSERT(aWorkerPrivate);
528 1 : MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
529 : aBehavior == WorkerThreadUnchangedBusyCount,
530 : "WorkerControlRunnables should not modify the busy count");
531 1 : }
532 : #endif
533 :
534 : nsresult
535 0 : WorkerControlRunnable::Cancel()
536 : {
537 0 : if (NS_FAILED(Run())) {
538 0 : NS_WARNING("WorkerControlRunnable::Run() failed.");
539 : }
540 :
541 0 : return WorkerRunnable::Cancel();
542 : }
543 :
544 : bool
545 1 : WorkerControlRunnable::DispatchInternal()
546 : {
547 2 : RefPtr<WorkerControlRunnable> runnable(this);
548 :
549 1 : if (mBehavior == WorkerThreadUnchangedBusyCount) {
550 0 : return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
551 : }
552 :
553 1 : if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
554 0 : return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
555 : }
556 :
557 1 : return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
558 : }
559 :
560 17 : NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
561 :
562 10 : WorkerMainThreadRunnable::WorkerMainThreadRunnable(
563 : WorkerPrivate* aWorkerPrivate,
564 10 : const nsACString& aTelemetryKey)
565 : : mozilla::Runnable("dom::workers::WorkerMainThreadRunnable")
566 : , mWorkerPrivate(aWorkerPrivate)
567 10 : , mTelemetryKey(aTelemetryKey)
568 : {
569 10 : mWorkerPrivate->AssertIsOnWorkerThread();
570 10 : }
571 :
572 : void
573 10 : WorkerMainThreadRunnable::Dispatch(Status aFailStatus, ErrorResult& aRv)
574 : {
575 10 : mWorkerPrivate->AssertIsOnWorkerThread();
576 :
577 10 : TimeStamp startTime = TimeStamp::NowLoRes();
578 :
579 19 : AutoSyncLoopHolder syncLoop(mWorkerPrivate, aFailStatus);
580 :
581 10 : mSyncLoopTarget = syncLoop.GetEventTarget();
582 10 : if (!mSyncLoopTarget) {
583 : // SyncLoop creation can fail if the worker is shutting down.
584 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
585 0 : return;
586 : }
587 :
588 19 : DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
589 10 : MOZ_ASSERT(NS_SUCCEEDED(rv),
590 : "Should only fail after xpcom-shutdown-threads and we're gone by then");
591 :
592 10 : bool success = syncLoop.Run();
593 :
594 9 : Telemetry::Accumulate(Telemetry::SYNC_WORKER_OPERATION, mTelemetryKey,
595 18 : static_cast<uint32_t>((TimeStamp::NowLoRes() - startTime)
596 18 : .ToMilliseconds()));
597 :
598 : Unused << startTime; // Shut the compiler up.
599 :
600 9 : if (!success) {
601 0 : aRv.ThrowUncatchableException();
602 : }
603 : }
604 :
605 : NS_IMETHODIMP
606 9 : WorkerMainThreadRunnable::Run()
607 : {
608 9 : AssertIsOnMainThread();
609 :
610 9 : bool runResult = MainThreadRun();
611 :
612 : RefPtr<MainThreadStopSyncLoopRunnable> response =
613 : new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
614 18 : mSyncLoopTarget.forget(),
615 27 : runResult);
616 :
617 9 : MOZ_ALWAYS_TRUE(response->Dispatch());
618 :
619 18 : return NS_OK;
620 : }
621 :
622 2 : WorkerCheckAPIExposureOnMainThreadRunnable::WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate):
623 : WorkerMainThreadRunnable(aWorkerPrivate,
624 2 : NS_LITERAL_CSTRING("WorkerCheckAPIExposureOnMainThread"))
625 2 : {}
626 :
627 2 : WorkerCheckAPIExposureOnMainThreadRunnable::~WorkerCheckAPIExposureOnMainThreadRunnable()
628 2 : {}
629 :
630 : bool
631 2 : WorkerCheckAPIExposureOnMainThreadRunnable::Dispatch()
632 : {
633 4 : ErrorResult rv;
634 2 : WorkerMainThreadRunnable::Dispatch(Terminating, rv);
635 2 : bool ok = !rv.Failed();
636 2 : rv.SuppressException();
637 4 : return ok;
638 : }
639 :
640 : bool
641 0 : WorkerSameThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
642 : {
643 : // We don't call WorkerRunnable::PreDispatch, because we're using
644 : // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
645 : // that PreDispatch is on the parent thread in that case.
646 0 : aWorkerPrivate->AssertIsOnWorkerThread();
647 0 : return true;
648 : }
649 :
650 : void
651 0 : WorkerSameThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
652 : bool aDispatchResult)
653 : {
654 : // We don't call WorkerRunnable::PostDispatch, because we're using
655 : // WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
656 : // that PostDispatch is on the parent thread in that case.
657 0 : aWorkerPrivate->AssertIsOnWorkerThread();
658 0 : if (aDispatchResult) {
659 0 : DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(true);
660 : // Should never fail since if this thread is still running, so should the
661 : // parent and it should be able to process a control runnable.
662 0 : MOZ_ASSERT(willIncrement);
663 : }
664 0 : }
665 :
666 0 : WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable(
667 0 : WorkerPrivate* aWorkerPrivate)
668 : : mozilla::Runnable("dom::workers::WorkerProxyToMainThreadRunnable")
669 0 : , mWorkerPrivate(aWorkerPrivate)
670 : {
671 0 : MOZ_ASSERT(mWorkerPrivate);
672 0 : mWorkerPrivate->AssertIsOnWorkerThread();
673 0 : }
674 :
675 0 : WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable()
676 0 : {}
677 :
678 : bool
679 0 : WorkerProxyToMainThreadRunnable::Dispatch()
680 : {
681 0 : mWorkerPrivate->AssertIsOnWorkerThread();
682 :
683 0 : if (NS_WARN_IF(!HoldWorker())) {
684 0 : RunBackOnWorkerThreadForCleanup();
685 0 : return false;
686 : }
687 :
688 0 : if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
689 0 : ReleaseWorker();
690 0 : RunBackOnWorkerThreadForCleanup();
691 0 : return false;
692 : }
693 :
694 0 : return true;
695 : }
696 :
697 : NS_IMETHODIMP
698 0 : WorkerProxyToMainThreadRunnable::Run()
699 : {
700 0 : AssertIsOnMainThread();
701 0 : RunOnMainThread();
702 0 : PostDispatchOnMainThread();
703 0 : return NS_OK;
704 : }
705 :
706 : void
707 0 : WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread()
708 : {
709 : class ReleaseRunnable final : public MainThreadWorkerControlRunnable
710 : {
711 : RefPtr<WorkerProxyToMainThreadRunnable> mRunnable;
712 :
713 : public:
714 0 : ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
715 : WorkerProxyToMainThreadRunnable* aRunnable)
716 0 : : MainThreadWorkerControlRunnable(aWorkerPrivate)
717 0 : , mRunnable(aRunnable)
718 : {
719 0 : MOZ_ASSERT(aRunnable);
720 0 : }
721 :
722 : // We must call RunBackOnWorkerThreadForCleanup() also if the runnable is
723 : // canceled.
724 : nsresult
725 0 : Cancel() override
726 : {
727 0 : WorkerRun(nullptr, mWorkerPrivate);
728 0 : return MainThreadWorkerControlRunnable::Cancel();
729 : }
730 :
731 : virtual bool
732 0 : WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
733 : {
734 0 : MOZ_ASSERT(aWorkerPrivate);
735 0 : aWorkerPrivate->AssertIsOnWorkerThread();
736 :
737 0 : if (mRunnable) {
738 0 : mRunnable->RunBackOnWorkerThreadForCleanup();
739 :
740 : // Let's release the worker thread.
741 0 : mRunnable->ReleaseWorker();
742 0 : mRunnable = nullptr;
743 : }
744 :
745 0 : return true;
746 : }
747 :
748 : private:
749 0 : ~ReleaseRunnable()
750 0 : {}
751 : };
752 :
753 : RefPtr<WorkerControlRunnable> runnable =
754 0 : new ReleaseRunnable(mWorkerPrivate, this);
755 0 : Unused << NS_WARN_IF(!runnable->Dispatch());
756 0 : }
757 :
758 : bool
759 0 : WorkerProxyToMainThreadRunnable::HoldWorker()
760 : {
761 0 : mWorkerPrivate->AssertIsOnWorkerThread();
762 0 : MOZ_ASSERT(!mWorkerHolder);
763 :
764 0 : class SimpleWorkerHolder final : public WorkerHolder
765 : {
766 : public:
767 0 : bool Notify(Status aStatus) override
768 : {
769 : // We don't care about the notification. We just want to keep the
770 : // mWorkerPrivate alive.
771 0 : return true;
772 : }
773 : };
774 :
775 0 : UniquePtr<WorkerHolder> workerHolder(new SimpleWorkerHolder());
776 0 : if (NS_WARN_IF(!workerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
777 0 : return false;
778 : }
779 :
780 0 : mWorkerHolder = Move(workerHolder);
781 0 : return true;
782 : }
783 :
784 : void
785 0 : WorkerProxyToMainThreadRunnable::ReleaseWorker()
786 : {
787 0 : mWorkerPrivate->AssertIsOnWorkerThread();
788 0 : MOZ_ASSERT(mWorkerHolder);
789 0 : mWorkerHolder = nullptr;
790 0 : }
|