/work/obj-fuzz/dist/include/mozilla/gfx/JobScheduler.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 MOZILLA_GFX_TASKSCHEDULER_H_ |
8 | | #define MOZILLA_GFX_TASKSCHEDULER_H_ |
9 | | |
10 | | #include "mozilla/RefPtr.h" |
11 | | #include "mozilla/gfx/Types.h" |
12 | | #include "mozilla/RefCounted.h" |
13 | | |
14 | | #ifdef WIN32 |
15 | | #include "mozilla/gfx/JobScheduler_win32.h" |
16 | | #else |
17 | | #include "mozilla/gfx/JobScheduler_posix.h" |
18 | | #endif |
19 | | |
20 | | #include <vector> |
21 | | |
22 | | namespace mozilla { |
23 | | namespace gfx { |
24 | | |
25 | | class MultiThreadedJobQueue; |
26 | | class SyncObject; |
27 | | class WorkerThread; |
28 | | |
29 | | class JobScheduler { |
30 | | public: |
31 | | /// Return one of the queues that the drawing worker threads pull from, chosen |
32 | | /// pseudo-randomly. |
33 | | static MultiThreadedJobQueue* GetDrawingQueue() |
34 | | { |
35 | | return sSingleton->mDrawingQueues[ |
36 | | sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size() |
37 | | ]; |
38 | | } |
39 | | |
40 | | /// Return one of the queues that the drawing worker threads pull from with a |
41 | | /// hash to choose the queue. |
42 | | /// |
43 | | /// Calling this function several times with the same hash will yield the same queue. |
44 | | static MultiThreadedJobQueue* GetDrawingQueue(uint32_t aHash) |
45 | 0 | { |
46 | 0 | return sSingleton->mDrawingQueues[ |
47 | 0 | aHash % sSingleton->mDrawingQueues.size() |
48 | 0 | ]; |
49 | 0 | } |
50 | | |
51 | | /// Return the task queue associated to the worker the task is pinned to if |
52 | | /// the task is pinned to a worker, or a random queue. |
53 | | static MultiThreadedJobQueue* GetQueueForJob(Job* aJob); |
54 | | |
55 | | /// Initialize the task scheduler with aNumThreads worker threads for drawing |
56 | | /// and aNumQueues task queues. |
57 | | /// |
58 | | /// The number of threads must be superior or equal to the number of queues |
59 | | /// (since for now a worker thread only pulls from one queue). |
60 | | static bool Init(uint32_t aNumThreads, uint32_t aNumQueues); |
61 | | |
62 | | /// Shut the scheduler down. |
63 | | /// |
64 | | /// This will block until worker threads are joined and deleted. |
65 | | static void ShutDown(); |
66 | | |
67 | | /// Returns true if there is a successfully initialized JobScheduler singleton. |
68 | | static bool IsEnabled() { return !!sSingleton; } |
69 | | |
70 | | /// Submit a task buffer to its associated queue. |
71 | | /// |
72 | | /// The caller looses ownership of the task buffer. |
73 | | static void SubmitJob(Job* aJobs); |
74 | | |
75 | | /// Convenience function to block the current thread until a given SyncObject |
76 | | /// is in the signaled state. |
77 | | /// |
78 | | /// The current thread will first try to steal jobs before blocking. |
79 | | static void Join(SyncObject* aCompletionSync); |
80 | | |
81 | | /// Process commands until the command buffer needs to block on a sync object, |
82 | | /// completes, yields, or encounters an error. |
83 | | /// |
84 | | /// Can be used on any thread. Worker threads basically loop over this, but the |
85 | | /// main thread can also dequeue pending task buffers and process them alongside |
86 | | /// the worker threads if it is about to block until completion anyway. |
87 | | /// |
88 | | /// The caller looses ownership of the task buffer. |
89 | | static JobStatus ProcessJob(Job* aJobs); |
90 | | |
91 | | protected: |
92 | | static JobScheduler* sSingleton; |
93 | | |
94 | | // queues of Job that are ready to be processed |
95 | | std::vector<MultiThreadedJobQueue*> mDrawingQueues; |
96 | | std::vector<WorkerThread*> mWorkerThreads; |
97 | | Atomic<uint32_t> mNextQueue; |
98 | | }; |
99 | | |
100 | | /// Jobs are not reference-counted because they don't have shared ownership. |
101 | | /// The ownership of tasks can change when they are passed to certain methods |
102 | | /// of JobScheduler and SyncObject. See the docuumentaion of these classes. |
103 | | class Job { |
104 | | public: |
105 | | Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread = nullptr); |
106 | | |
107 | | virtual ~Job(); |
108 | | |
109 | | virtual JobStatus Run() = 0; |
110 | | |
111 | | /// For use in JobScheduler::SubmitJob. Don't use it anywhere else. |
112 | | //already_AddRefed<SyncObject> GetAndResetStartSync(); |
113 | | SyncObject* GetStartSync() { return mStartSync; } |
114 | | |
115 | | bool IsPinnedToAThread() const { return !!mPinToThread; } |
116 | | |
117 | | WorkerThread* GetWorkerThread() { return mPinToThread; } |
118 | | |
119 | | protected: |
120 | | // An intrusive linked list of tasks waiting for a sync object to enter the |
121 | | // signaled state. When the task is not waiting for a sync object, mNextWaitingJob |
122 | | // should be null. This is only accessed from the thread that owns the task. |
123 | | Job* mNextWaitingJob; |
124 | | |
125 | | RefPtr<SyncObject> mStartSync; |
126 | | RefPtr<SyncObject> mCompletionSync; |
127 | | WorkerThread* mPinToThread; |
128 | | |
129 | | friend class SyncObject; |
130 | | }; |
131 | | |
132 | | class EventObject; |
133 | | |
134 | | /// This task will set an EventObject. |
135 | | /// |
136 | | /// Typically used as the final task, so that the main thread can block on the |
137 | | /// corresponfing EventObject until all of the tasks are processed. |
138 | | class SetEventJob : public Job |
139 | | { |
140 | | public: |
141 | | explicit SetEventJob(EventObject* aEvent, |
142 | | SyncObject* aStart, SyncObject* aCompletion = nullptr, |
143 | | WorkerThread* aPinToWorker = nullptr); |
144 | | |
145 | | ~SetEventJob(); |
146 | | |
147 | | JobStatus Run() override; |
148 | | |
149 | 0 | EventObject* GetEvent() { return mEvent; } |
150 | | |
151 | | protected: |
152 | | RefPtr<EventObject> mEvent; |
153 | | }; |
154 | | |
155 | | /// A synchronization object that can be used to express dependencies and ordering between |
156 | | /// tasks. |
157 | | /// |
158 | | /// Jobs can register to SyncObjects in order to asynchronously wait for a signal. |
159 | | /// In practice, Job objects usually start with a sync object (startSyc) and end |
160 | | /// with another one (completionSync). |
161 | | /// a Job never gets processed before its startSync is in the signaled state, and |
162 | | /// signals its completionSync as soon as it finishes. This is how dependencies |
163 | | /// between tasks is expressed. |
164 | | class SyncObject final : public external::AtomicRefCounted<SyncObject> { |
165 | | public: |
166 | | MOZ_DECLARE_REFCOUNTED_TYPENAME(SyncObject) |
167 | | |
168 | | /// Create a synchronization object. |
169 | | /// |
170 | | /// aNumPrerequisites represents the number of times the object must be signaled |
171 | | /// before actually entering the signaled state (in other words, it means the |
172 | | /// number of dependencies of this sync object). |
173 | | /// |
174 | | /// Explicitly specifying the number of prerequisites when creating sync objects |
175 | | /// makes it easy to start scheduling some of the prerequisite tasks while |
176 | | /// creating the others, which is how we typically use the task scheduler. |
177 | | /// Automatically determining the number of prerequisites using Job's constructor |
178 | | /// brings the risk that the sync object enters the signaled state while we |
179 | | /// are still adding prerequisites which is hard to fix without using muteces. |
180 | | explicit SyncObject(uint32_t aNumPrerequisites = 1); |
181 | | |
182 | | ~SyncObject(); |
183 | | |
184 | | /// Attempt to register a task. |
185 | | /// |
186 | | /// If the sync object is already in the signaled state, the buffer is *not* |
187 | | /// registered and the sync object does not take ownership of the task. |
188 | | /// If the object is not yet in the signaled state, it takes ownership of |
189 | | /// the task and places it in a list of pending tasks. |
190 | | /// Pending tasks will not be processed by the worker thread. |
191 | | /// When the SyncObject reaches the signaled state, it places the pending |
192 | | /// tasks back in the available buffer queue, so that they can be |
193 | | /// scheduled again. |
194 | | /// |
195 | | /// Returns true if the SyncOject is not already in the signaled state. |
196 | | /// This means that if this method returns true, the SyncObject has taken |
197 | | /// ownership of the Job. |
198 | | bool Register(Job* aJob); |
199 | | |
200 | | /// Signal the SyncObject. |
201 | | /// |
202 | | /// This decrements an internal counter. The sync object reaches the signaled |
203 | | /// state when the counter gets to zero. |
204 | | void Signal(); |
205 | | |
206 | | /// Returns true if mSignals is equal to zero. In other words, returns true |
207 | | /// if all prerequisite tasks have already signaled the sync object. |
208 | | bool IsSignaled(); |
209 | | |
210 | | /// Asserts that the number of added prerequisites is equal to the number |
211 | | /// specified in the constructor (does nothin in release builds). |
212 | | void FreezePrerequisites(); |
213 | | |
214 | | private: |
215 | | // Called by Job's constructor |
216 | | void AddSubsequent(Job* aJob); |
217 | | void AddPrerequisite(Job* aJob); |
218 | | |
219 | | void AddWaitingJob(Job* aJob); |
220 | | |
221 | | void SubmitWaitingJobs(); |
222 | | |
223 | | Atomic<int32_t> mSignals; |
224 | | Atomic<Job*> mFirstWaitingJob; |
225 | | |
226 | | #ifdef DEBUG |
227 | | uint32_t mNumPrerequisites; |
228 | | Atomic<uint32_t> mAddedPrerequisites; |
229 | | #endif |
230 | | |
231 | | friend class Job; |
232 | | friend class JobScheduler; |
233 | | }; |
234 | | |
235 | | /// Base class for worker threads. |
236 | | class WorkerThread |
237 | | { |
238 | | public: |
239 | | static WorkerThread* Create(MultiThreadedJobQueue* aJobQueue); |
240 | | |
241 | 0 | virtual ~WorkerThread() {} |
242 | | |
243 | | void Run(); |
244 | | |
245 | | MultiThreadedJobQueue* GetJobQueue() { return mQueue; } |
246 | | |
247 | | protected: |
248 | | explicit WorkerThread(MultiThreadedJobQueue* aJobQueue); |
249 | | |
250 | | virtual void SetName(const char* aName) {} |
251 | | |
252 | | MultiThreadedJobQueue* mQueue; |
253 | | }; |
254 | | |
255 | | } // namespace |
256 | | } // namespace |
257 | | |
258 | | #endif |