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 #ifndef mozilla_dom_workers_workerrunnable_h__
8 #define mozilla_dom_workers_workerrunnable_h__
12 #include "MainThreadUtils.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/dom/WorkerRef.h"
16 #include "mozilla/dom/WorkerStatus.h"
18 #include "nsICancelableRunnable.h"
19 #include "nsIRunnable.h"
20 #include "nsISupports.h"
21 #include "nsStringFwd.h"
22 #include "nsThreadUtils.h"
27 class nsIGlobalObject
;
37 // Use this runnable to communicate from the worker to its parent or vice-versa.
38 // The busy count must be taken into consideration and declared at construction
40 class WorkerRunnable
: public nsIRunnable
, public nsICancelableRunnable
{
42 enum TargetAndBusyBehavior
{
43 // Target the main thread for top-level workers, otherwise target the
44 // WorkerThread of the worker's parent. No change to the busy count.
45 ParentThreadUnchangedBusyCount
,
47 // Target the thread where the worker event loop runs. The busy count will
48 // be incremented before dispatching and decremented (asynchronously) after
50 WorkerThreadModifyBusyCount
,
52 // Target the thread where the worker event loop runs. The busy count will
53 // not be modified in any way. Besides worker-internal runnables this is
54 // almost always the wrong choice.
55 WorkerThreadUnchangedBusyCount
59 // The WorkerPrivate that this runnable is associated with.
60 WorkerPrivate
* mWorkerPrivate
;
63 TargetAndBusyBehavior mBehavior
;
65 // It's unclear whether or not Cancel() is supposed to work when called on any
66 // thread. To be safe we're using an atomic but it's likely overkill.
67 Atomic
<uint32_t> mCanceled
;
70 // Whether or not Cancel() is currently being called from inside the Run()
71 // method. Avoids infinite recursion when a subclass calls Run() from inside
72 // Cancel(). Only checked and modified on the target thread.
73 bool mCallingCancelWithinRun
;
76 NS_DECL_THREADSAFE_ISUPPORTS
78 // If you override Cancel() then you'll need to either call the base class
79 // Cancel() method or override IsCanceled() so that the Run() method bails out
81 // Cancel() should not be called more than once and we throw
82 // NS_ERROR_UNEXPECTED if it is. If you override it, ensure to call the base
83 // class method first and bail out on failure to avoid unexpected side
85 nsresult
Cancel() override
;
87 // The return value is true if and only if both PreDispatch and
88 // DispatchInternal return true.
91 // See above note about Cancel().
92 // TODO: Check if we can remove the possibility to override IsCanceled.
93 virtual bool IsCanceled() const { return mCanceled
!= 0; }
95 // True if this runnable is handled by running JavaScript in some global that
96 // could possibly be a debuggee, and thus needs to be deferred when the target
97 // is paused in the debugger, until the JavaScript invocation in progress has
98 // run to completion. Examples are MessageEventRunnable and
99 // ReportErrorRunnable. These runnables are segregated into separate
100 // ThrottledEventQueues, which the debugger pauses.
102 // Note that debugger runnables do not fall in this category, since we don't
103 // support debugging the debugger server at the moment.
104 virtual bool IsDebuggeeRunnable() const { return false; }
106 static WorkerRunnable
* FromRunnable(nsIRunnable
* aRunnable
);
109 WorkerRunnable(WorkerPrivate
* aWorkerPrivate
,
110 TargetAndBusyBehavior aBehavior
= WorkerThreadModifyBusyCount
)
114 : mWorkerPrivate(aWorkerPrivate
),
115 mBehavior(aBehavior
),
117 mCallingCancelWithinRun(false) {
121 // This class is reference counted.
122 virtual ~WorkerRunnable() = default;
124 // Returns true if this runnable should be dispatched to the debugger queue,
125 // and false otherwise.
126 virtual bool IsDebuggerRunnable() const;
128 nsIGlobalObject
* DefaultGlobalObject() const;
130 // By default asserts that Dispatch() is being called on the right thread
131 // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
132 // Also increments the busy count of |mWorkerPrivate| if targeting the
134 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
);
136 // By default asserts that Dispatch() is being called on the right thread
137 // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
138 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
139 bool aDispatchResult
);
141 // May be implemented by subclasses if desired if they need to do some sort of
142 // setup before we try to set up our JSContext and compartment for real.
143 // Typically the only thing that should go in here is creation of the worker's
146 // If false is returned, WorkerRun will not be called at all. PostRun will
147 // still be called, with false passed for aRunResult.
148 virtual bool PreRun(WorkerPrivate
* aWorkerPrivate
);
150 // Must be implemented by subclasses. Called on the target thread. The return
151 // value will be passed to PostRun(). The JSContext passed in here comes from
152 // an AutoJSAPI (or AutoEntryScript) that we set up on the stack. If
153 // mBehavior is ParentThreadUnchangedBusyCount, it is in the compartment of
154 // mWorkerPrivate's reflector (i.e. the worker object in the parent thread),
155 // unless that reflector is null, in which case it's in the compartment of the
156 // parent global (which is the compartment reflector would have been in), or
157 // in the null compartment if there is no parent global. For other mBehavior
158 // values, we're running on the worker thread and aCx is in whatever
159 // compartment GetCurrentWorkerThreadJSContext() was in when
160 // nsIRunnable::Run() got called. This is actually important for cases when a
161 // runnable spins a syncloop and wants everything that happens during the
162 // syncloop to happen in the compartment that runnable set up (which may, for
163 // example, be a debugger sandbox compartment!). If aCx wasn't in a
164 // compartment to start with, aCx will be in either the debugger global's
165 // compartment or the worker's global's compartment depending on whether
166 // IsDebuggerRunnable() is true.
168 // Immediately after WorkerRun returns, the caller will assert that either it
169 // returns false or there is no exception pending on aCx. Then it will report
170 // any pending exceptions on aCx.
171 virtual bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) = 0;
173 // By default asserts that Run() (and WorkerRun()) were called on the correct
174 // thread. Also sends an asynchronous message to the ParentThread if the
175 // busy count was previously modified in PreDispatch().
177 // The aCx passed here is the same one as was passed to WorkerRun and is
178 // still in the same compartment. PostRun implementations must NOT leave an
179 // exception on the JSContext and must not run script, because the incoming
180 // JSContext may be in the null compartment.
181 virtual void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
184 virtual bool DispatchInternal();
186 // Calling Run() directly is not supported. Just call Dispatch() and
187 // WorkerRun() will be called on the correct thread automatically.
191 // This runnable is used to send a message to a worker debugger.
192 class WorkerDebuggerRunnable
: public WorkerRunnable
{
194 explicit WorkerDebuggerRunnable(WorkerPrivate
* aWorkerPrivate
)
195 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
197 virtual ~WorkerDebuggerRunnable() = default;
200 virtual bool IsDebuggerRunnable() const override
{ return true; }
202 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) final
{
203 AssertIsOnMainThread();
208 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
209 bool aDispatchResult
) override
;
212 // This runnable is used to send a message directly to a worker's sync loop.
213 class WorkerSyncRunnable
: public WorkerRunnable
{
215 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
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
);
222 WorkerSyncRunnable(WorkerPrivate
* aWorkerPrivate
,
223 nsCOMPtr
<nsIEventTarget
>&& aSyncLoopTarget
);
225 virtual ~WorkerSyncRunnable();
227 virtual bool DispatchInternal() override
;
230 // This runnable is identical to WorkerSyncRunnable except it is meant to be
231 // created on and dispatched from the main thread only. Its WorkerRun/PostRun
232 // will run on the worker thread.
233 class MainThreadWorkerSyncRunnable
: public WorkerSyncRunnable
{
235 // Passing null for aSyncLoopTarget is allowed and will result in the behavior
236 // of a normal WorkerRunnable.
237 MainThreadWorkerSyncRunnable(WorkerPrivate
* aWorkerPrivate
,
238 nsIEventTarget
* aSyncLoopTarget
)
239 : WorkerSyncRunnable(aWorkerPrivate
, aSyncLoopTarget
) {
240 AssertIsOnMainThread();
243 MainThreadWorkerSyncRunnable(WorkerPrivate
* aWorkerPrivate
,
244 nsCOMPtr
<nsIEventTarget
>&& aSyncLoopTarget
)
245 : WorkerSyncRunnable(aWorkerPrivate
, std::move(aSyncLoopTarget
)) {
246 AssertIsOnMainThread();
249 virtual ~MainThreadWorkerSyncRunnable() = default;
252 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
253 AssertIsOnMainThread();
257 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
258 bool aDispatchResult
) override
;
261 // This runnable is processed as soon as it is received by the worker,
262 // potentially running before previously queued runnables and perhaps even with
263 // other JS code executing on the stack. These runnables must not alter the
264 // state of the JS runtime and should only twiddle state values. The busy count
265 // is never modified.
266 class WorkerControlRunnable
: public WorkerRunnable
{
267 friend class WorkerPrivate
;
270 WorkerControlRunnable(WorkerPrivate
* aWorkerPrivate
,
271 TargetAndBusyBehavior aBehavior
)
275 : WorkerRunnable(aWorkerPrivate
, aBehavior
) {
279 virtual ~WorkerControlRunnable() = default;
281 nsresult
Cancel() override
;
284 NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerControlRunnable
, WorkerRunnable
)
287 virtual bool DispatchInternal() override
;
289 // Should only be called by WorkerPrivate::DoRunLoop.
290 using WorkerRunnable::Cancel
;
293 // A convenience class for WorkerRunnables that are originated on the main
295 class MainThreadWorkerRunnable
: public WorkerRunnable
{
297 explicit MainThreadWorkerRunnable(WorkerPrivate
* aWorkerPrivate
)
298 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {
299 AssertIsOnMainThread();
302 virtual ~MainThreadWorkerRunnable() = default;
304 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
305 AssertIsOnMainThread();
309 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
310 bool aDispatchResult
) override
{
311 AssertIsOnMainThread();
315 // A convenience class for WorkerControlRunnables that originate on the main
317 class MainThreadWorkerControlRunnable
: public WorkerControlRunnable
{
319 explicit MainThreadWorkerControlRunnable(WorkerPrivate
* aWorkerPrivate
)
320 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
322 virtual ~MainThreadWorkerControlRunnable() = default;
324 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
325 AssertIsOnMainThread();
329 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
330 bool aDispatchResult
) override
{
331 AssertIsOnMainThread();
335 // A WorkerRunnable that should be dispatched from the worker to itself for
336 // async tasks. This will increment the busy count PostDispatch() (only if
337 // dispatch was successful) and decrement it in PostRun().
339 // Async tasks will almost always want to use this since
340 // a WorkerSameThreadRunnable keeps the Worker from being GCed.
341 class WorkerSameThreadRunnable
: public WorkerRunnable
{
343 explicit WorkerSameThreadRunnable(WorkerPrivate
* aWorkerPrivate
)
344 : WorkerRunnable(aWorkerPrivate
, WorkerThreadModifyBusyCount
) {}
346 virtual ~WorkerSameThreadRunnable() = default;
348 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
;
350 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
351 bool aDispatchResult
) override
;
353 // We just delegate PostRun to WorkerRunnable, since it does exactly
357 // Base class for the runnable objects, which makes a synchronous call to
358 // dispatch the tasks from the worker thread to the main thread.
360 // Note that the derived class must override MainThreadRun.
361 class WorkerMainThreadRunnable
: public Runnable
{
363 WorkerPrivate
* mWorkerPrivate
;
364 nsCOMPtr
<nsIEventTarget
> mSyncLoopTarget
;
365 const nsCString mTelemetryKey
;
367 explicit WorkerMainThreadRunnable(WorkerPrivate
* aWorkerPrivate
,
368 const nsACString
& aTelemetryKey
);
369 ~WorkerMainThreadRunnable();
371 virtual bool MainThreadRun() = 0;
374 // Dispatch the runnable to the main thread. If dispatch to main thread
375 // fails, or if the worker is in a state equal or greater of aFailStatus, an
376 // error will be reported on aRv. Normally you want to use 'Canceling' for
377 // aFailStatus, except if you want an infallible runnable. In this case, use
379 // In that case the error MUST be propagated out to script.
380 void Dispatch(WorkerStatus aFailStatus
, ErrorResult
& aRv
);
383 NS_IMETHOD
Run() override
;
386 // This runnable is an helper class for dispatching something from a worker
387 // thread to the main-thread and back to the worker-thread. During this
388 // operation, this class will keep the worker alive.
389 // The purpose of RunBackOnWorkerThreadForCleanup() must be used, as the name
390 // says, only to release resources, no JS has to be executed, no timers, or
391 // other things. The reason of such limitations is that, in order to execute
392 // this method in any condition (also when the worker is shutting down), a
393 // Control Runnable is used, and, this could generate a reordering of existing
395 class WorkerProxyToMainThreadRunnable
: public Runnable
{
397 WorkerProxyToMainThreadRunnable();
399 virtual ~WorkerProxyToMainThreadRunnable();
401 // First this method is called on the main-thread.
402 virtual void RunOnMainThread(WorkerPrivate
* aWorkerPrivate
) = 0;
404 // After this second method is called on the worker-thread.
405 virtual void RunBackOnWorkerThreadForCleanup(
406 WorkerPrivate
* aWorkerPrivate
) = 0;
409 bool Dispatch(WorkerPrivate
* aWorkerPrivate
);
411 virtual bool ForMessaging() const { return false; }
414 NS_IMETHOD
Run() override
;
416 void PostDispatchOnMainThread();
418 void ReleaseWorker();
420 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
423 // This runnable is used to stop a sync loop and it's meant to be used on the
424 // main-thread only. As sync loops keep the busy count incremented as long as
425 // they run this runnable does not modify the busy count
427 class MainThreadStopSyncLoopRunnable
: public WorkerSyncRunnable
{
431 // Passing null for aSyncLoopTarget is not allowed.
432 MainThreadStopSyncLoopRunnable(WorkerPrivate
* aWorkerPrivate
,
433 nsCOMPtr
<nsIEventTarget
>&& aSyncLoopTarget
,
436 // By default StopSyncLoopRunnables cannot be canceled since they could leave
437 // a sync loop spinning forever.
438 nsresult
Cancel() override
;
441 virtual ~MainThreadStopSyncLoopRunnable() = default;
444 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) final
{
445 AssertIsOnMainThread();
449 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
450 bool aDispatchResult
) override
;
452 virtual bool WorkerRun(JSContext
* aCx
,
453 WorkerPrivate
* aWorkerPrivate
) override
;
455 bool DispatchInternal() final
;
458 // Runnables handled by content JavaScript (MessageEventRunnable, JavaScript
459 // error reports, and so on) must not be delivered while that content is in the
460 // midst of being debugged; the debuggee must be allowed to complete its current
461 // JavaScript invocation and return to its own event loop. Only then is it
462 // prepared for messages sent from the worker.
464 // Runnables that need to be deferred in this way should inherit from this
465 // class. They will be routed to mMainThreadDebuggeeEventTarget, which is paused
466 // while the window is suspended, as it is whenever the debugger spins its
467 // nested event loop. When the debugger leaves its nested event loop, it resumes
468 // the window, so that mMainThreadDebuggeeEventTarget will resume delivering
469 // runnables from the worker when control returns to the main event loop.
471 // When a page enters the bfcache, it freezes all its workers. Since a frozen
472 // worker processes only control runnables, it doesn't take any special
473 // consideration to prevent WorkerDebuggeeRunnables sent from child to parent
474 // workers from running; they'll never run anyway. But WorkerDebuggeeRunnables
475 // from a top-level frozen worker to its parent window must not be delivered
476 // either, even as the main thread event loop continues to spin. Thus, freezing
477 // a top-level worker also pauses mMainThreadDebuggeeEventTarget.
478 class WorkerDebuggeeRunnable
: public WorkerRunnable
{
480 WorkerDebuggeeRunnable(
481 WorkerPrivate
* aWorkerPrivate
,
482 TargetAndBusyBehavior aBehavior
= ParentThreadUnchangedBusyCount
)
483 : WorkerRunnable(aWorkerPrivate
, aBehavior
) {}
485 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
;
488 // This override is deliberately private: it doesn't make sense to call it if
489 // we know statically that we are a WorkerDebuggeeRunnable.
490 bool IsDebuggeeRunnable() const override
{ return true; }
492 // Runnables sent upwards, to the content window or parent worker, must keep
493 // their sender alive until they are delivered: they check back with the
494 // sender in case it has been terminated after having dispatched the runnable
495 // (in which case it should not be acted upon); and runnables sent to content
496 // wait until delivery to determine the target window, since
497 // WorkerPrivate::GetWindow may only be used on the main thread.
499 // Runnables sent downwards, from content to a worker or from a worker to a
500 // child, keep the sender alive because they are WorkerThreadModifyBusyCount
501 // runnables, and so leave this null.
502 RefPtr
<ThreadSafeWorkerRef
> mSender
;
506 } // namespace mozilla
508 #endif // mozilla_dom_workers_workerrunnable_h__