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