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/. */
7 #if !defined(AbstractThread_h_)
8 # define AbstractThread_h_
10 # include "mozilla/AlreadyAddRefed.h"
11 # include "mozilla/ThreadLocal.h"
13 # include "nsISerialEventTarget.h"
14 # include "nsISupports.h"
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.
32 * At present, the supported AbstractThread implementations are TaskQueue,
33 * AbstractThread::MainThread() and XPCOMThreadWrapper which can wrap any
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.
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.
45 class AbstractThread
: public nsISerialEventTarget
{
47 // Returns the AbstractThread that the caller is currently running in, or null
48 // if the caller is not running in an AbstractThread.
49 static AbstractThread
* GetCurrent() { return sCurrentThreadTLS
.get(); }
51 AbstractThread(bool aSupportsTailDispatch
)
52 : mSupportsTailDispatch(aSupportsTailDispatch
) {}
54 NS_DECL_THREADSAFE_ISUPPORTS
56 // We don't use NS_DECL_NSIEVENTTARGET so that we can remove the default
57 // |flags| parameter from Dispatch. Otherwise, a single-argument Dispatch call
58 // would be ambiguous.
59 using nsISerialEventTarget::IsOnCurrentThread
;
60 NS_IMETHOD_(bool) IsOnCurrentThreadInfallible(void) override
;
61 NS_IMETHOD
IsOnCurrentThread(bool* _retval
) override
;
62 NS_IMETHOD
Dispatch(already_AddRefed
<nsIRunnable
> event
,
63 uint32_t flags
) override
;
64 NS_IMETHOD
DispatchFromScript(nsIRunnable
* event
, uint32_t flags
) override
;
65 NS_IMETHOD
DelayedDispatch(already_AddRefed
<nsIRunnable
> event
,
66 uint32_t delay
) override
;
68 enum DispatchReason
{ NormalDispatch
, TailDispatch
};
69 virtual nsresult
Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
,
70 DispatchReason aReason
= NormalDispatch
) = 0;
72 virtual bool IsCurrentThreadIn() const = 0;
74 // Returns a TaskDispatcher that will dispatch its tasks when the currently-
75 // running tasks pops off the stack.
77 // May only be called when running within the it is invoked up, and only on
78 // threads which support it.
79 virtual TaskDispatcher
& TailDispatcher() = 0;
81 // Returns true if we have tail tasks scheduled, or if this isn't known.
82 // Returns false if we definitely don't have any tail tasks.
83 virtual bool MightHaveTailTasks() { return true; }
85 // Returns true if the tail dispatcher is available. In certain edge cases
86 // like shutdown, it might not be.
87 virtual bool IsTailDispatcherAvailable() { return true; }
89 // Helper functions for methods on the tail TasklDispatcher. These check
90 // HasTailTasks to avoid allocating a TailDispatcher if it isn't
92 nsresult
TailDispatchTasksFor(AbstractThread
* aThread
);
93 bool HasTailTasksFor(AbstractThread
* aThread
);
95 // Returns true if this supports the tail dispatcher.
96 bool SupportsTailDispatch() const { return mSupportsTailDispatch
; }
98 // Returns true if this thread requires all dispatches originating from
99 // aThread go through the tail dispatcher.
100 bool RequiresTailDispatch(AbstractThread
* aThread
) const;
101 bool RequiresTailDispatchFromCurrentThread() const;
103 virtual nsIEventTarget
* AsEventTarget() { MOZ_CRASH("Not an event target!"); }
105 // Returns the non-DocGroup version of AbstractThread on the main thread.
106 // A DocGroup-versioned one is available in
107 // DispatcherTrait::AbstractThreadFor(). Note:
108 // DispatcherTrait::AbstractThreadFor() SHALL be used when possible.
109 static AbstractThread
* MainThread();
111 // Must be called exactly once during startup.
112 static void InitTLS();
113 static void InitMainThread();
114 static void ShutdownMainThread();
116 void DispatchStateChange(already_AddRefed
<nsIRunnable
> aRunnable
);
118 static void DispatchDirectTask(already_AddRefed
<nsIRunnable
> aRunnable
);
121 virtual ~AbstractThread() = default;
122 static MOZ_THREAD_LOCAL(AbstractThread
*) sCurrentThreadTLS
;
124 // True if we want to require that every task dispatched from tasks running in
125 // this queue go through our queue's tail dispatcher.
126 const bool mSupportsTailDispatch
;
129 } // namespace mozilla