/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__ |