Coverage Report

Created: 2018-09-25 14:53

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