michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_workers_workerrunnable_h__ michael@0: #define mozilla_dom_workers_workerrunnable_h__ michael@0: michael@0: #include "Workers.h" michael@0: michael@0: #include "nsICancelableRunnable.h" michael@0: michael@0: #include "mozilla/Atomics.h" michael@0: #include "nsISupportsImpl.h" michael@0: michael@0: class JSContext; michael@0: class nsIEventTarget; michael@0: michael@0: BEGIN_WORKERS_NAMESPACE michael@0: michael@0: class WorkerPrivate; michael@0: michael@0: // Use this runnable to communicate from the worker to its parent or vice-versa. michael@0: // The busy count must be taken into consideration and declared at construction michael@0: // time. michael@0: class WorkerRunnable : public nsICancelableRunnable michael@0: { michael@0: public: michael@0: enum TargetAndBusyBehavior { michael@0: // Target the main thread for top-level workers, otherwise target the michael@0: // WorkerThread of the worker's parent. No change to the busy count. michael@0: ParentThreadUnchangedBusyCount, michael@0: michael@0: // Target the thread where the worker event loop runs. The busy count will michael@0: // be incremented before dispatching and decremented (asynchronously) after michael@0: // running. michael@0: WorkerThreadModifyBusyCount, michael@0: michael@0: // Target the thread where the worker event loop runs. The busy count will michael@0: // not be modified in any way. Besides worker-internal runnables this is michael@0: // almost always the wrong choice. michael@0: WorkerThreadUnchangedBusyCount michael@0: }; michael@0: michael@0: protected: michael@0: // The WorkerPrivate that this runnable is associated with. michael@0: WorkerPrivate* mWorkerPrivate; michael@0: michael@0: // See above. michael@0: TargetAndBusyBehavior mBehavior; michael@0: michael@0: // It's unclear whether or not Cancel() is supposed to work when called on any michael@0: // thread. To be safe we're using an atomic but it's likely overkill. michael@0: Atomic mCanceled; michael@0: michael@0: private: michael@0: // Whether or not Cancel() is currently being called from inside the Run() michael@0: // method. Avoids infinite recursion when a subclass calls Run() from inside michael@0: // Cancel(). Only checked and modified on the target thread. michael@0: bool mCallingCancelWithinRun; michael@0: michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: // If you override Cancel() then you'll need to either call the base class michael@0: // Cancel() method or override IsCanceled() so that the Run() method bails out michael@0: // appropriately. michael@0: NS_DECL_NSICANCELABLERUNNABLE michael@0: michael@0: // Passing a JSContext here is required for the WorkerThreadModifyBusyCount michael@0: // behavior. It also guarantees that any failure (false return) will throw an michael@0: // exception on the given context. If a context is not passed then failures michael@0: // must be dealt with by the caller. michael@0: bool michael@0: Dispatch(JSContext* aCx); michael@0: michael@0: // See above note about Cancel(). michael@0: virtual bool michael@0: IsCanceled() const michael@0: { michael@0: return mCanceled != 0; michael@0: } michael@0: michael@0: static WorkerRunnable* michael@0: FromRunnable(nsIRunnable* aRunnable); michael@0: michael@0: protected: michael@0: WorkerRunnable(WorkerPrivate* aWorkerPrivate, TargetAndBusyBehavior aBehavior) michael@0: #ifdef DEBUG michael@0: ; michael@0: #else michael@0: : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0), michael@0: mCallingCancelWithinRun(false) michael@0: { } michael@0: #endif michael@0: michael@0: // This class is reference counted. michael@0: virtual ~WorkerRunnable() michael@0: { } michael@0: michael@0: // By default asserts that Dispatch() is being called on the right thread michael@0: // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise). michael@0: // Also increments the busy count of |mWorkerPrivate| if targeting the michael@0: // WorkerThread. michael@0: virtual bool michael@0: PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate); michael@0: michael@0: // By default asserts that Dispatch() is being called on the right thread michael@0: // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise). michael@0: // Also reports any Dispatch() failures as an exception on |aCx|, and michael@0: // busy count if targeting the WorkerThread and Dispatch() failed. michael@0: virtual void michael@0: PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aDispatchResult); michael@0: michael@0: // Must be implemented by subclasses. Called on the target thread. michael@0: virtual bool michael@0: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0; michael@0: michael@0: // By default asserts that Run() (and WorkerRun()) were called on the correct michael@0: // thread. Any failures (false return from WorkerRun) are reported on |aCx|. michael@0: // Also sends an asynchronous message to the ParentThread if the busy michael@0: // count was previously modified in PreDispatch(). michael@0: virtual void michael@0: PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult); michael@0: michael@0: virtual bool michael@0: DispatchInternal(); michael@0: michael@0: // Calling Run() directly is not supported. Just call Dispatch() and michael@0: // WorkerRun() will be called on the correct thread automatically. michael@0: NS_DECL_NSIRUNNABLE michael@0: }; michael@0: michael@0: // This runnable is used to send a message directly to a worker's sync loop. michael@0: class WorkerSyncRunnable : public WorkerRunnable michael@0: { michael@0: protected: michael@0: nsCOMPtr mSyncLoopTarget; michael@0: michael@0: // Passing null for aSyncLoopTarget is allowed and will result in the behavior michael@0: // of a normal WorkerRunnable. michael@0: WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, michael@0: nsIEventTarget* aSyncLoopTarget); michael@0: michael@0: WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, michael@0: already_AddRefed&& aSyncLoopTarget); michael@0: michael@0: virtual ~WorkerSyncRunnable(); michael@0: michael@0: private: michael@0: virtual bool michael@0: DispatchInternal() MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // This runnable is identical to WorkerSyncRunnable except it is meant to be michael@0: // used on the main thread only. michael@0: class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable michael@0: { michael@0: protected: michael@0: // Passing null for aSyncLoopTarget is allowed and will result in the behavior michael@0: // of a normal WorkerRunnable. michael@0: MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, michael@0: nsIEventTarget* aSyncLoopTarget) michael@0: : WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, michael@0: already_AddRefed&& aSyncLoopTarget) michael@0: : WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: virtual ~MainThreadWorkerSyncRunnable() michael@0: { } michael@0: michael@0: private: michael@0: virtual bool michael@0: PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE michael@0: { michael@0: AssertIsOnMainThread(); michael@0: return true; michael@0: } michael@0: michael@0: virtual void michael@0: PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aDispatchResult) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // This runnable is used to stop a sync loop . As sync loops keep the busy count michael@0: // incremented as long as they run this runnable does not modify the busy count michael@0: // in any way. michael@0: class StopSyncLoopRunnable : public WorkerSyncRunnable michael@0: { michael@0: bool mResult; michael@0: michael@0: public: michael@0: // Passing null for aSyncLoopTarget is not allowed. michael@0: StopSyncLoopRunnable(WorkerPrivate* aWorkerPrivate, michael@0: already_AddRefed&& aSyncLoopTarget, michael@0: bool aResult); michael@0: michael@0: // By default StopSyncLoopRunnables cannot be canceled since they could leave michael@0: // a sync loop spinning forever. michael@0: NS_DECL_NSICANCELABLERUNNABLE michael@0: michael@0: protected: michael@0: virtual ~StopSyncLoopRunnable() michael@0: { } michael@0: michael@0: // Called on the worker thread to set an exception on the context if mResult michael@0: // is false. Override if you need an exception. michael@0: virtual void michael@0: MaybeSetException(JSContext* aCx) michael@0: { } michael@0: michael@0: private: michael@0: virtual bool michael@0: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: DispatchInternal() MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // This runnable is identical to StopSyncLoopRunnable except it is meant to be michael@0: // used on the main thread only. michael@0: class MainThreadStopSyncLoopRunnable : public StopSyncLoopRunnable michael@0: { michael@0: public: michael@0: // Passing null for aSyncLoopTarget is not allowed. michael@0: MainThreadStopSyncLoopRunnable( michael@0: WorkerPrivate* aWorkerPrivate, michael@0: already_AddRefed&& aSyncLoopTarget, michael@0: bool aResult) michael@0: : StopSyncLoopRunnable(aWorkerPrivate, Move(aSyncLoopTarget), aResult) michael@0: { michael@0: AssertIsOnMainThread(); michael@0: } michael@0: michael@0: protected: michael@0: virtual ~MainThreadStopSyncLoopRunnable() michael@0: { } michael@0: michael@0: private: michael@0: virtual bool michael@0: PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE michael@0: { michael@0: AssertIsOnMainThread(); michael@0: return true; michael@0: } michael@0: michael@0: virtual void michael@0: PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aDispatchResult) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // This runnable is processed as soon as it is received by the worker, michael@0: // potentially running before previously queued runnables and perhaps even with michael@0: // other JS code executing on the stack. These runnables must not alter the michael@0: // state of the JS runtime and should only twiddle state values. The busy count michael@0: // is never modified. michael@0: class WorkerControlRunnable : public WorkerRunnable michael@0: { michael@0: friend class WorkerPrivate; michael@0: michael@0: protected: michael@0: WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, michael@0: TargetAndBusyBehavior aBehavior) michael@0: #ifdef DEBUG michael@0: ; michael@0: #else michael@0: : WorkerRunnable(aWorkerPrivate, aBehavior) michael@0: { } michael@0: #endif michael@0: michael@0: virtual ~WorkerControlRunnable() michael@0: { } michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: private: michael@0: virtual bool michael@0: DispatchInternal() MOZ_OVERRIDE; michael@0: michael@0: // Should only be called by WorkerPrivate::DoRunLoop. michael@0: using WorkerRunnable::Cancel; michael@0: }; michael@0: michael@0: // A convenience class for WorkerControlRunnables that originate on the main michael@0: // thread. michael@0: class MainThreadWorkerControlRunnable : public WorkerControlRunnable michael@0: { michael@0: protected: michael@0: MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate) michael@0: : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) michael@0: { } michael@0: michael@0: virtual ~MainThreadWorkerControlRunnable() michael@0: { } michael@0: michael@0: virtual bool michael@0: PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE michael@0: { michael@0: AssertIsOnMainThread(); michael@0: return true; michael@0: } michael@0: michael@0: virtual void michael@0: PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aDispatchResult) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: // A WorkerRunnable that should be dispatched from the worker to itself for michael@0: // async tasks. This will increment the busy count PostDispatch() (only if michael@0: // dispatch was successful) and decrement it in PostRun(). michael@0: // michael@0: // Async tasks will almost always want to use this since michael@0: // a WorkerSameThreadRunnable keeps the Worker from being GCed. michael@0: class WorkerSameThreadRunnable : public WorkerRunnable michael@0: { michael@0: protected: michael@0: WorkerSameThreadRunnable(WorkerPrivate* aWorkerPrivate) michael@0: : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) michael@0: { } michael@0: michael@0: virtual ~WorkerSameThreadRunnable() michael@0: { } michael@0: michael@0: virtual bool michael@0: PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aDispatchResult) MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, michael@0: bool aRunResult) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: END_WORKERS_NAMESPACE michael@0: michael@0: #endif // mozilla_dom_workers_workerrunnable_h__