Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/workers/WorkerRunnable.h
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
#ifndef mozilla_dom_workers_workerrunnable_h__
8
#define mozilla_dom_workers_workerrunnable_h__
9
10
#include "mozilla/dom/WorkerCommon.h"
11
#include "mozilla/dom/WorkerRef.h"
12
13
#include "nsICancelableRunnable.h"
14
15
#include "mozilla/Atomics.h"
16
#include "nsISupportsImpl.h"
17
#include "nsThreadUtils.h" /* nsRunnable */
18
19
struct JSContext;
20
class nsIEventTarget;
21
22
namespace mozilla {
23
24
class ErrorResult;
25
26
namespace dom {
27
28
class WorkerPrivate;
29
30
// Use this runnable to communicate from the worker to its parent or vice-versa.
31
// The busy count must be taken into consideration and declared at construction
32
// time.
33
class WorkerRunnable : public nsIRunnable,
34
                       public nsICancelableRunnable
35
{
36
public:
37
  enum TargetAndBusyBehavior {
38
    // Target the main thread for top-level workers, otherwise target the
39
    // WorkerThread of the worker's parent. No change to the busy count.
40
    ParentThreadUnchangedBusyCount,
41
42
    // Target the thread where the worker event loop runs. The busy count will
43
    // be incremented before dispatching and decremented (asynchronously) after
44
    // running.
45
    WorkerThreadModifyBusyCount,
46
47
    // Target the thread where the worker event loop runs. The busy count will
48
    // not be modified in any way. Besides worker-internal runnables this is
49
    // almost always the wrong choice.
50
    WorkerThreadUnchangedBusyCount
51
  };
52
53
protected:
54
  // The WorkerPrivate that this runnable is associated with.
55
  WorkerPrivate* mWorkerPrivate;
56
57
  // See above.
58
  TargetAndBusyBehavior mBehavior;
59
60
  // It's unclear whether or not Cancel() is supposed to work when called on any
61
  // thread. To be safe we're using an atomic but it's likely overkill.
62
  Atomic<uint32_t> mCanceled;
63
64
private:
65
  // Whether or not Cancel() is currently being called from inside the Run()
66
  // method. Avoids infinite recursion when a subclass calls Run() from inside
67
  // Cancel(). Only checked and modified on the target thread.
68
  bool mCallingCancelWithinRun;
69
70
public:
71
  NS_DECL_THREADSAFE_ISUPPORTS
72
73
  // If you override Cancel() then you'll need to either call the base class
74
  // Cancel() method or override IsCanceled() so that the Run() method bails out
75
  // appropriately.
76
  nsresult
77
  Cancel() override;
78
79
  // The return value is true if and only if both PreDispatch and
80
  // DispatchInternal return true.
81
  bool
82
  Dispatch();
83
84
  // See above note about Cancel().
85
  virtual bool
86
  IsCanceled() const
87
  {
88
    return mCanceled != 0;
89
  }
90
91
  static WorkerRunnable*
92
  FromRunnable(nsIRunnable* aRunnable);
93
94
protected:
95
  WorkerRunnable(WorkerPrivate* aWorkerPrivate,
96
                 TargetAndBusyBehavior aBehavior = WorkerThreadModifyBusyCount)
97
#ifdef DEBUG
98
  ;
99
#else
100
  : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
101
    mCallingCancelWithinRun(false)
102
  { }
103
#endif
104
105
  // This class is reference counted.
106
  virtual ~WorkerRunnable()
107
  { }
108
109
  // Returns true if this runnable should be dispatched to the debugger queue,
110
  // and false otherwise.
111
  virtual bool
112
  IsDebuggerRunnable() const;
113
114
  nsIGlobalObject*
115
  DefaultGlobalObject() const;
116
117
  // By default asserts that Dispatch() is being called on the right thread
118
  // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
119
  // Also increments the busy count of |mWorkerPrivate| if targeting the
120
  // WorkerThread.
121
  virtual bool
122
  PreDispatch(WorkerPrivate* aWorkerPrivate);
123
124
  // By default asserts that Dispatch() is being called on the right thread
125
  // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
126
  virtual void
127
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult);
128
129
  // May be implemented by subclasses if desired if they need to do some sort of
130
  // setup before we try to set up our JSContext and compartment for real.
131
  // Typically the only thing that should go in here is creation of the worker's
132
  // global.
133
  //
134
  // If false is returned, WorkerRun will not be called at all.  PostRun will
135
  // still be called, with false passed for aRunResult.
136
  virtual bool
137
  PreRun(WorkerPrivate* aWorkerPrivate);
138
139
  // Must be implemented by subclasses. Called on the target thread.  The return
140
  // value will be passed to PostRun().  The JSContext passed in here comes from
141
  // an AutoJSAPI (or AutoEntryScript) that we set up on the stack.  If
142
  // mBehavior is ParentThreadUnchangedBusyCount, it is in the compartment of
143
  // mWorkerPrivate's reflector (i.e. the worker object in the parent thread),
144
  // unless that reflector is null, in which case it's in the compartment of the
145
  // parent global (which is the compartment reflector would have been in), or
146
  // in the null compartment if there is no parent global.  For other mBehavior
147
  // values, we're running on the worker thread and aCx is in whatever
148
  // compartment GetCurrentWorkerThreadJSContext() was in when
149
  // nsIRunnable::Run() got called.  This is actually important for cases when a
150
  // runnable spins a syncloop and wants everything that happens during the
151
  // syncloop to happen in the compartment that runnable set up (which may, for
152
  // example, be a debugger sandbox compartment!).  If aCx wasn't in a
153
  // compartment to start with, aCx will be in either the debugger global's
154
  // compartment or the worker's global's compartment depending on whether
155
  // IsDebuggerRunnable() is true.
156
  //
157
  // Immediately after WorkerRun returns, the caller will assert that either it
158
  // returns false or there is no exception pending on aCx.  Then it will report
159
  // any pending exceptions on aCx.
160
  virtual bool
161
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
162
163
  // By default asserts that Run() (and WorkerRun()) were called on the correct
164
  // thread.  Also sends an asynchronous message to the ParentThread if the
165
  // busy count was previously modified in PreDispatch().
166
  //
167
  // The aCx passed here is the same one as was passed to WorkerRun and is
168
  // still in the same compartment.  PostRun implementations must NOT leave an
169
  // exception on the JSContext and must not run script, because the incoming
170
  // JSContext may be in the null compartment.
171
  virtual void
172
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
173
          bool aRunResult);
174
175
  virtual bool
176
  DispatchInternal();
177
178
  // Calling Run() directly is not supported. Just call Dispatch() and
179
  // WorkerRun() will be called on the correct thread automatically.
180
  NS_DECL_NSIRUNNABLE
181
};
182
183
// This runnable is used to send a message to a worker debugger.
184
class WorkerDebuggerRunnable : public WorkerRunnable
185
{
186
protected:
187
  explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
188
  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
189
0
  {
190
0
  }
191
192
  virtual ~WorkerDebuggerRunnable()
193
  { }
194
195
private:
196
  virtual bool
197
  IsDebuggerRunnable() const override
198
0
  {
199
0
    return true;
200
0
  }
201
202
  bool
203
  PreDispatch(WorkerPrivate* aWorkerPrivate) final
204
0
  {
205
0
    AssertIsOnMainThread();
206
0
207
0
    return true;
208
0
  }
209
210
  virtual void
211
  PostDispatch(WorkerPrivate* aWorkerPrivate,
212
               bool aDispatchResult) override;
213
};
214
215
// This runnable is used to send a message directly to a worker's sync loop.
216
class WorkerSyncRunnable : public WorkerRunnable
217
{
218
protected:
219
  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
220
221
  // Passing null for aSyncLoopTarget is allowed and will result in the behavior
222
  // of a normal WorkerRunnable.
223
  WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
224
                     nsIEventTarget* aSyncLoopTarget);
225
226
  WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
227
                     already_AddRefed<nsIEventTarget>&& aSyncLoopTarget);
228
229
  virtual ~WorkerSyncRunnable();
230
231
  virtual bool
232
  DispatchInternal() override;
233
};
234
235
// This runnable is identical to WorkerSyncRunnable except it is meant to be
236
// created on and dispatched from the main thread only.  Its WorkerRun/PostRun
237
// will run on the worker thread.
238
class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable
239
{
240
protected:
241
  // Passing null for aSyncLoopTarget is allowed and will result in the behavior
242
  // of a normal WorkerRunnable.
243
  MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
244
                               nsIEventTarget* aSyncLoopTarget)
245
  : WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
246
0
  {
247
0
    AssertIsOnMainThread();
248
0
  }
249
250
  MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
251
                               already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
252
  : WorkerSyncRunnable(aWorkerPrivate, std::move(aSyncLoopTarget))
253
  {
254
    AssertIsOnMainThread();
255
  }
256
257
  virtual ~MainThreadWorkerSyncRunnable()
258
  { }
259
260
private:
261
  virtual bool
262
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
263
0
  {
264
0
    AssertIsOnMainThread();
265
0
    return true;
266
0
  }
267
268
  virtual void
269
  PostDispatch(WorkerPrivate* aWorkerPrivate,
270
               bool aDispatchResult) override;
271
};
272
273
// This runnable is processed as soon as it is received by the worker,
274
// potentially running before previously queued runnables and perhaps even with
275
// other JS code executing on the stack. These runnables must not alter the
276
// state of the JS runtime and should only twiddle state values. The busy count
277
// is never modified.
278
class WorkerControlRunnable : public WorkerRunnable
279
{
280
  friend class WorkerPrivate;
281
282
protected:
283
  WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
284
                        TargetAndBusyBehavior aBehavior)
285
#ifdef DEBUG
286
  ;
287
#else
288
  : WorkerRunnable(aWorkerPrivate, aBehavior)
289
  { }
290
#endif
291
292
  virtual ~WorkerControlRunnable()
293
  { }
294
295
  nsresult
296
  Cancel() override;
297
298
public:
299
  NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerControlRunnable, WorkerRunnable)
300
301
private:
302
  virtual bool
303
  DispatchInternal() override;
304
305
  // Should only be called by WorkerPrivate::DoRunLoop.
306
  using WorkerRunnable::Cancel;
307
};
308
309
// A convenience class for WorkerRunnables that are originated on the main
310
// thread.
311
class MainThreadWorkerRunnable : public WorkerRunnable
312
{
313
protected:
314
  explicit MainThreadWorkerRunnable(WorkerPrivate* aWorkerPrivate)
315
  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
316
  {
317
    AssertIsOnMainThread();
318
  }
319
320
  virtual ~MainThreadWorkerRunnable()
321
  {}
322
323
  virtual bool
324
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
325
  {
326
    AssertIsOnMainThread();
327
    return true;
328
  }
329
330
  virtual void
331
  PostDispatch(WorkerPrivate* aWorkerPrivate,
332
               bool aDispatchResult) override
333
  {
334
    AssertIsOnMainThread();
335
  }
336
};
337
338
// A convenience class for WorkerControlRunnables that originate on the main
339
// thread.
340
class MainThreadWorkerControlRunnable : public WorkerControlRunnable
341
{
342
protected:
343
  explicit MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate)
344
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
345
  { }
346
347
  virtual ~MainThreadWorkerControlRunnable()
348
  { }
349
350
  virtual bool
351
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
352
  {
353
    AssertIsOnMainThread();
354
    return true;
355
  }
356
357
  virtual void
358
  PostDispatch(WorkerPrivate* aWorkerPrivate,
359
               bool aDispatchResult) override
360
  {
361
    AssertIsOnMainThread();
362
  }
363
};
364
365
// A WorkerRunnable that should be dispatched from the worker to itself for
366
// async tasks. This will increment the busy count PostDispatch() (only if
367
// dispatch was successful) and decrement it in PostRun().
368
//
369
// Async tasks will almost always want to use this since
370
// a WorkerSameThreadRunnable keeps the Worker from being GCed.
371
class WorkerSameThreadRunnable : public WorkerRunnable
372
{
373
protected:
374
  explicit WorkerSameThreadRunnable(WorkerPrivate* aWorkerPrivate)
375
  : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
376
  { }
377
378
  virtual ~WorkerSameThreadRunnable()
379
  { }
380
381
  virtual bool
382
  PreDispatch(WorkerPrivate* aWorkerPrivate) override;
383
384
  virtual void
385
  PostDispatch(WorkerPrivate* aWorkerPrivate,
386
               bool aDispatchResult) override;
387
388
  // We just delegate PostRun to WorkerRunnable, since it does exactly
389
  // what we want.
390
};
391
392
// Base class for the runnable objects, which makes a synchronous call to
393
// dispatch the tasks from the worker thread to the main thread.
394
//
395
// Note that the derived class must override MainThreadRun.
396
class WorkerMainThreadRunnable : public Runnable
397
{
398
protected:
399
  WorkerPrivate* mWorkerPrivate;
400
  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
401
  const nsCString mTelemetryKey;
402
403
  explicit WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
404
                                    const nsACString& aTelemetryKey);
405
  ~WorkerMainThreadRunnable() {}
406
407
  virtual bool MainThreadRun() = 0;
408
409
public:
410
  // Dispatch the runnable to the main thread.  If dispatch to main thread
411
  // fails, or if the worker is in a state equal or greater of aFailStatus, an
412
  // error will be reported on aRv. Normally you want to use 'Canceling' for
413
  // aFailStatus, except if you want an infallible runnable. In this case, use
414
  // 'Killing'.
415
  // In that case the error MUST be propagated out to script.
416
  void Dispatch(WorkerStatus aFailStatus, ErrorResult& aRv);
417
418
private:
419
  NS_IMETHOD Run() override;
420
};
421
422
// This runnable is an helper class for dispatching something from a worker
423
// thread to the main-thread and back to the worker-thread. During this
424
// operation, this class will keep the worker alive.
425
// The purpose of RunBackOnWorkerThreadForCleanup() must be used, as the name
426
// says, only to release resources, no JS has to be executed, no timers, or
427
// other things. The reason of such limitations is that, in order to execute
428
// this method in any condition (also when the worker is shutting down), a
429
// Control Runnable is used, and, this could generate a reordering of existing
430
// runnables.
431
class WorkerProxyToMainThreadRunnable : public Runnable
432
{
433
protected:
434
  WorkerProxyToMainThreadRunnable();
435
436
  virtual ~WorkerProxyToMainThreadRunnable();
437
438
  // First this method is called on the main-thread.
439
  virtual void RunOnMainThread(WorkerPrivate* aWorkerPrivate) = 0;
440
441
  // After this second method is called on the worker-thread.
442
  virtual void
443
  RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) = 0;
444
445
public:
446
  bool Dispatch(WorkerPrivate* aWorkerPrivate);
447
448
private:
449
  NS_IMETHOD Run() override;
450
451
  void PostDispatchOnMainThread();
452
453
  void ReleaseWorker();
454
455
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
456
};
457
458
// This runnable is used to stop a sync loop and it's meant to be used on the
459
// main-thread only. As sync loops keep the busy count incremented as long as
460
// they run this runnable does not modify the busy count
461
// in any way.
462
class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable
463
{
464
  bool mResult;
465
466
public:
467
  // Passing null for aSyncLoopTarget is not allowed.
468
  MainThreadStopSyncLoopRunnable(
469
                               WorkerPrivate* aWorkerPrivate,
470
                               already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
471
                               bool aResult);
472
473
  // By default StopSyncLoopRunnables cannot be canceled since they could leave
474
  // a sync loop spinning forever.
475
  nsresult
476
  Cancel() override;
477
478
protected:
479
  virtual ~MainThreadStopSyncLoopRunnable()
480
  { }
481
482
private:
483
  bool
484
  PreDispatch(WorkerPrivate* aWorkerPrivate) final
485
0
  {
486
0
    AssertIsOnMainThread();
487
0
    return true;
488
0
  }
489
490
  virtual void
491
  PostDispatch(WorkerPrivate* aWorkerPrivate,
492
               bool aDispatchResult) override;
493
494
  virtual bool
495
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
496
497
  bool
498
  DispatchInternal() final;
499
};
500
501
} // dom namespace
502
} // mozilla namespace
503
504
#endif // mozilla_dom_workers_workerrunnable_h__