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