/work/obj-fuzz/dist/include/mozilla/TaskQueue.h
Line | Count | Source (jump to first uncovered line) |
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 | | #ifndef TaskQueue_h_ |
8 | | #define TaskQueue_h_ |
9 | | |
10 | | #include "mozilla/Monitor.h" |
11 | | #include "mozilla/MozPromise.h" |
12 | | #include "mozilla/RefPtr.h" |
13 | | #include "mozilla/TaskDispatcher.h" |
14 | | #include "mozilla/Unused.h" |
15 | | |
16 | | #include <queue> |
17 | | |
18 | | #include "nsThreadUtils.h" |
19 | | |
20 | | class nsIEventTarget; |
21 | | class nsIRunnable; |
22 | | |
23 | | namespace mozilla { |
24 | | |
25 | | typedef MozPromise<bool, bool, false> ShutdownPromise; |
26 | | |
27 | | // Abstracts executing runnables in order on an arbitrary event target. The |
28 | | // runnables dispatched to the TaskQueue will be executed in the order in which |
29 | | // they're received, and are guaranteed to not be executed concurrently. |
30 | | // They may be executed on different threads, and a memory barrier is used |
31 | | // to make this threadsafe for objects that aren't already threadsafe. |
32 | | // |
33 | | // Note, since a TaskQueue can also be converted to an nsIEventTarget using |
34 | | // WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues. |
35 | | // Consider these three TaskQueues: |
36 | | // |
37 | | // TQ1 dispatches to the main thread |
38 | | // TQ2 dispatches to TQ1 |
39 | | // TQ3 dispatches to TQ1 |
40 | | // |
41 | | // This ensures there is only ever a single runnable from the entire chain on |
42 | | // the main thread. It also ensures that TQ2 and TQ3 only have a single runnable |
43 | | // in TQ1 at any time. |
44 | | // |
45 | | // This arrangement lets you prioritize work by dispatching runnables directly |
46 | | // to TQ1. You can issue many runnables for important work. Meanwhile the TQ2 |
47 | | // and TQ3 work will always execute at most one runnable and then yield. |
48 | | // |
49 | | // A TaskQueue does not require explicit shutdown, however it provides a |
50 | | // BeginShutdown() method that places TaskQueue in a shut down state and returns |
51 | | // a promise that gets resolved once all pending tasks have completed |
52 | | class TaskQueue : public AbstractThread |
53 | | { |
54 | | class EventTargetWrapper; |
55 | | |
56 | | public: |
57 | | explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget, |
58 | | bool aSupportsTailDispatch = false); |
59 | | |
60 | | TaskQueue(already_AddRefed<nsIEventTarget> aTarget, |
61 | | const char* aName, |
62 | | bool aSupportsTailDispatch = false); |
63 | | |
64 | | TaskDispatcher& TailDispatcher() override; |
65 | | |
66 | 0 | TaskQueue* AsTaskQueue() override { return this; } |
67 | | |
68 | | MOZ_MUST_USE nsresult |
69 | | Dispatch(already_AddRefed<nsIRunnable> aRunnable, |
70 | | DispatchReason aReason = NormalDispatch) override |
71 | 0 | { |
72 | 0 | nsCOMPtr<nsIRunnable> r = aRunnable; |
73 | 0 | { |
74 | 0 | MonitorAutoLock mon(mQueueMonitor); |
75 | 0 | return DispatchLocked(/* passed by ref */r, aReason); |
76 | 0 | } |
77 | 0 | // If the ownership of |r| is not transferred in DispatchLocked() due to |
78 | 0 | // dispatch failure, it will be deleted here outside the lock. We do so |
79 | 0 | // since the destructor of the runnable might access TaskQueue and result |
80 | 0 | // in deadlocks. |
81 | 0 | } |
82 | | |
83 | | // Prevent a GCC warning about the other overload of Dispatch being hidden. |
84 | | using AbstractThread::Dispatch; |
85 | | |
86 | | // Puts the queue in a shutdown state and returns immediately. The queue will |
87 | | // remain alive at least until all the events are drained, because the Runners |
88 | | // hold a strong reference to the task queue, and one of them is always held |
89 | | // by the target event queue when the task queue is non-empty. |
90 | | // |
91 | | // The returned promise is resolved when the queue goes empty. |
92 | | RefPtr<ShutdownPromise> BeginShutdown(); |
93 | | |
94 | | // Blocks until all task finish executing. |
95 | | void AwaitIdle(); |
96 | | |
97 | | // Blocks until the queue is flagged for shutdown and all tasks have finished |
98 | | // executing. |
99 | | void AwaitShutdownAndIdle(); |
100 | | |
101 | | bool IsEmpty(); |
102 | | |
103 | | // Returns true if the current thread is currently running a Runnable in |
104 | | // the task queue. |
105 | | bool IsCurrentThreadIn() override; |
106 | | |
107 | | // Create a new nsIEventTarget wrapper object that dispatches to this |
108 | | // TaskQueue. |
109 | | already_AddRefed<nsISerialEventTarget> WrapAsEventTarget(); |
110 | | |
111 | | protected: |
112 | | virtual ~TaskQueue(); |
113 | | |
114 | | |
115 | | // Blocks until all task finish executing. Called internally by methods |
116 | | // that need to wait until the task queue is idle. |
117 | | // mQueueMonitor must be held. |
118 | | void AwaitIdleLocked(); |
119 | | |
120 | | nsresult DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable, |
121 | | DispatchReason aReason = NormalDispatch); |
122 | | |
123 | | void MaybeResolveShutdown() |
124 | 0 | { |
125 | 0 | mQueueMonitor.AssertCurrentThreadOwns(); |
126 | 0 | if (mIsShutdown && !mIsRunning) { |
127 | 0 | mShutdownPromise.ResolveIfExists(true, __func__); |
128 | 0 | mTarget = nullptr; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | nsCOMPtr<nsIEventTarget> mTarget; |
133 | | |
134 | | // Monitor that protects the queue and mIsRunning; |
135 | | Monitor mQueueMonitor; |
136 | | |
137 | | // Queue of tasks to run. |
138 | | std::queue<nsCOMPtr<nsIRunnable>> mTasks; |
139 | | |
140 | | // The thread currently running the task queue. We store a reference |
141 | | // to this so that IsCurrentThreadIn() can tell if the current thread |
142 | | // is the thread currently running in the task queue. |
143 | | // |
144 | | // This may be read on any thread, but may only be written on mRunningThread. |
145 | | // The thread can't die while we're running in it, and we only use it for |
146 | | // pointer-comparison with the current thread anyway - so we make it atomic |
147 | | // and don't refcount it. |
148 | | Atomic<PRThread*> mRunningThread; |
149 | | |
150 | | // RAII class that gets instantiated for each dispatched task. |
151 | | class AutoTaskGuard : public AutoTaskDispatcher |
152 | | { |
153 | | public: |
154 | | explicit AutoTaskGuard(TaskQueue* aQueue) |
155 | | : AutoTaskDispatcher(/* aIsTailDispatcher = */ true), mQueue(aQueue) |
156 | | , mLastCurrentThread(nullptr) |
157 | 0 | { |
158 | 0 | // NB: We don't hold the lock to aQueue here. Don't do anything that |
159 | 0 | // might require it. |
160 | 0 | MOZ_ASSERT(!mQueue->mTailDispatcher); |
161 | 0 | mQueue->mTailDispatcher = this; |
162 | 0 |
|
163 | 0 | mLastCurrentThread = sCurrentThreadTLS.get(); |
164 | 0 | sCurrentThreadTLS.set(aQueue); |
165 | 0 |
|
166 | 0 | MOZ_ASSERT(mQueue->mRunningThread == nullptr); |
167 | 0 | mQueue->mRunningThread = GetCurrentPhysicalThread(); |
168 | 0 | } |
169 | | |
170 | | ~AutoTaskGuard() |
171 | 0 | { |
172 | 0 | DrainDirectTasks(); |
173 | 0 |
|
174 | 0 | MOZ_ASSERT(mQueue->mRunningThread == GetCurrentPhysicalThread()); |
175 | 0 | mQueue->mRunningThread = nullptr; |
176 | 0 |
|
177 | 0 | sCurrentThreadTLS.set(mLastCurrentThread); |
178 | 0 | mQueue->mTailDispatcher = nullptr; |
179 | 0 | } |
180 | | |
181 | | private: |
182 | | TaskQueue* mQueue; |
183 | | AbstractThread* mLastCurrentThread; |
184 | | }; |
185 | | |
186 | | TaskDispatcher* mTailDispatcher; |
187 | | |
188 | | // True if we've dispatched an event to the target to execute events from |
189 | | // the queue. |
190 | | bool mIsRunning; |
191 | | |
192 | | // True if we've started our shutdown process. |
193 | | bool mIsShutdown; |
194 | | MozPromiseHolder<ShutdownPromise> mShutdownPromise; |
195 | | |
196 | | // The name of this TaskQueue. Useful when debugging dispatch failures. |
197 | | const char* const mName; |
198 | | |
199 | | class Runner : public Runnable { |
200 | | public: |
201 | | explicit Runner(TaskQueue* aQueue) |
202 | | : Runnable("TaskQueue::Runner") |
203 | | , mQueue(aQueue) |
204 | 0 | { |
205 | 0 | } |
206 | | NS_IMETHOD Run() override; |
207 | | private: |
208 | | RefPtr<TaskQueue> mQueue; |
209 | | }; |
210 | | }; |
211 | | |
212 | | } // namespace mozilla |
213 | | |
214 | | #endif // TaskQueue_h_ |