WorkerRunnable.h 16 KB

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