Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/2d/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
0
  {
35
0
    return sSingleton->mDrawingQueues[
36
0
      sSingleton->mNextQueue++ % sSingleton->mDrawingQueues.size()
37
0
    ];
38
0
  }
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
  {
46
    return sSingleton->mDrawingQueues[
47
      aHash % sSingleton->mDrawingQueues.size()
48
    ];
49
  }
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
0
  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
0
  SyncObject* GetStartSync() { return mStartSync; }
114
115
0
  bool IsPinnedToAThread() const { return !!mPinToThread; }
116
117
0
  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
  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
  virtual ~WorkerThread() {}
242
243
  void Run();
244
245
0
  MultiThreadedJobQueue* GetJobQueue() { return mQueue; }
246
247
protected:
248
  explicit WorkerThread(MultiThreadedJobQueue* aJobQueue);
249
250
0
  virtual void SetName(const char* aName) {}
251
252
  MultiThreadedJobQueue* mQueue;
253
};
254
255
} // namespace
256
} // namespace
257
258
#endif