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