LCOV - code coverage report
Current view: top level - dom/workers - WorkerRunnable.h (source / functions) Hit Total Coverage
Test: output.info Lines: 25 63 39.7 %
Date: 2017-07-14 16:53:18 Functions: 11 33 33.3 %
Legend: Lines: hit not hit

          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__

Generated by: LCOV version 1.13