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