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 #if !defined(AbstractThread_h_) 8 # define AbstractThread_h_ 9 10 # include "mozilla/RefPtr.h" 11 # include "mozilla/ThreadLocal.h" 12 # include "nscore.h" 13 # include "nsIRunnable.h" 14 # include "nsISerialEventTarget.h" 15 # include "nsISupportsImpl.h" 16 # include "nsIThread.h" 17 18 namespace mozilla { 19 20 class TaskQueue; 21 class TaskDispatcher; 22 23 /* 24 * We often want to run tasks on a target that guarantees that events will never 25 * run in parallel. There are various target types that achieve this - namely 26 * nsIThread and TaskQueue. Note that nsIThreadPool (which implements 27 * nsIEventTarget) does not have this property, so we do not want to use 28 * nsIEventTarget for this purpose. This class encapsulates the specifics of 29 * the structures we might use here and provides a consistent interface. 30 * 31 * At present, the supported AbstractThread implementations are TaskQueue, 32 * AbstractThread::MainThread() and XPCOMThreadWrapper which can wrap any 33 * nsThread. 34 * 35 * The primary use of XPCOMThreadWrapper is to allow any threads to provide 36 * Direct Task dispatching which is similar (but not identical to) the microtask 37 * semantics of JS promises. Instantiating a XPCOMThreadWrapper on the current 38 * nsThread is sufficient to enable direct task dispatching. 39 * 40 * You shouldn't use pointers when comparing AbstractThread or nsIThread to 41 * determine if you are currently on the thread, but instead use the 42 * nsISerialEventTarget::IsOnCurrentThread() method. 43 */ 44 class AbstractThread : public nsISerialEventTarget { 45 public: 46 // Returns the AbstractThread that the caller is currently running in, or null 47 // if the caller is not running in an AbstractThread. GetCurrent()48 static AbstractThread* GetCurrent() { return sCurrentThreadTLS.get(); } 49 AbstractThread(bool aSupportsTailDispatch)50 AbstractThread(bool aSupportsTailDispatch) 51 : mSupportsTailDispatch(aSupportsTailDispatch) {} 52 53 // Returns an AbstractThread wrapper of a nsIThread. 54 static already_AddRefed<AbstractThread> CreateXPCOMThreadWrapper( 55 nsIThread* aThread, bool aRequireTailDispatch, bool aOnThread = false); 56 57 NS_DECL_THREADSAFE_ISUPPORTS 58 59 // We don't use NS_DECL_NSIEVENTTARGET so that we can remove the default 60 // |flags| parameter from Dispatch. Otherwise, a single-argument Dispatch call 61 // would be ambiguous. 62 NS_IMETHOD_(bool) IsOnCurrentThreadInfallible(void) override; 63 NS_IMETHOD IsOnCurrentThread(bool* _retval) override; 64 NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> event, 65 uint32_t flags) override; 66 NS_IMETHOD DispatchFromScript(nsIRunnable* event, uint32_t flags) override; 67 NS_IMETHOD DelayedDispatch(already_AddRefed<nsIRunnable> event, 68 uint32_t delay) override; 69 70 enum DispatchReason { NormalDispatch, TailDispatch }; 71 virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable, 72 DispatchReason aReason = NormalDispatch) = 0; 73 74 virtual bool IsCurrentThreadIn() const = 0; 75 76 // Returns a TaskDispatcher that will dispatch its tasks when the currently- 77 // running tasks pops off the stack. 78 // 79 // May only be called when running within the it is invoked up, and only on 80 // threads which support it. 81 virtual TaskDispatcher& TailDispatcher() = 0; 82 83 // Returns true if we have tail tasks scheduled, or if this isn't known. 84 // Returns false if we definitely don't have any tail tasks. MightHaveTailTasks()85 virtual bool MightHaveTailTasks() { return true; } 86 87 // Returns true if the tail dispatcher is available. In certain edge cases 88 // like shutdown, it might not be. IsTailDispatcherAvailable()89 virtual bool IsTailDispatcherAvailable() { return true; } 90 91 // Helper functions for methods on the tail TasklDispatcher. These check 92 // HasTailTasks to avoid allocating a TailDispatcher if it isn't 93 // needed. 94 nsresult TailDispatchTasksFor(AbstractThread* aThread); 95 bool HasTailTasksFor(AbstractThread* aThread); 96 97 // Returns true if this supports the tail dispatcher. SupportsTailDispatch()98 bool SupportsTailDispatch() const { return mSupportsTailDispatch; } 99 100 // Returns true if this thread requires all dispatches originating from 101 // aThread go through the tail dispatcher. 102 bool RequiresTailDispatch(AbstractThread* aThread) const; 103 bool RequiresTailDispatchFromCurrentThread() const; 104 AsEventTarget()105 virtual nsIEventTarget* AsEventTarget() { MOZ_CRASH("Not an event target!"); } 106 107 // Returns the non-DocGroup version of AbstractThread on the main thread. 108 // A DocGroup-versioned one is available in 109 // DispatcherTrait::AbstractThreadFor(). Note: 110 // DispatcherTrait::AbstractThreadFor() SHALL be used when possible. 111 static AbstractThread* MainThread(); 112 113 // Must be called exactly once during startup. 114 static void InitTLS(); 115 static void InitMainThread(); 116 static void ShutdownMainThread(); 117 118 void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable); 119 120 static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable); 121 122 protected: 123 virtual ~AbstractThread() = default; 124 static MOZ_THREAD_LOCAL(AbstractThread*) sCurrentThreadTLS; 125 126 // True if we want to require that every task dispatched from tasks running in 127 // this queue go through our queue's tail dispatcher. 128 const bool mSupportsTailDispatch; 129 }; 130 131 } // namespace mozilla 132 133 #endif 134