Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / TaskController.h
blob58b0bbde01b17b273f0aa0179a17577ad2576190
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 #ifndef mozilla_TaskController_h
8 #define mozilla_TaskController_h
10 #include "mozilla/CondVar.h"
11 #include "mozilla/IdlePeriodState.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/Mutex.h"
14 #include "mozilla/StaticMutex.h"
15 #include "mozilla/TimeStamp.h"
16 #include "mozilla/EventQueue.h"
17 #include "nsISupportsImpl.h"
18 #include "nsIEventTarget.h"
20 #include <atomic>
21 #include <memory>
22 #include <vector>
23 #include <set>
24 #include <list>
25 #include <stack>
27 class nsIRunnable;
28 class nsIThreadObserver;
30 namespace mozilla {
32 class Task;
33 class TaskController;
34 class PerformanceCounter;
35 class PerformanceCounterState;
37 const EventQueuePriority kDefaultPriorityValue = EventQueuePriority::Normal;
39 // This file contains the core classes to access the Gecko scheduler. The
40 // scheduler forms a graph of prioritize tasks, and is responsible for ensuring
41 // the execution of tasks or their dependencies in order of inherited priority.
43 // The core class is the 'Task' class. The task class describes a single unit of
44 // work. Users scheduling work implement this class and are required to
45 // reimplement the 'Run' function in order to do work.
47 // The TaskManager class is reimplemented by users that require
48 // the ability to reprioritize or suspend tasks.
50 // The TaskController is responsible for scheduling the work itself. The AddTask
51 // function is used to schedule work. The ReprioritizeTask function may be used
52 // to change the priority of a task already in the task graph, without
53 // unscheduling it.
55 // The TaskManager is the baseclass used to atomically manage a large set of
56 // tasks. API users reimplementing TaskManager may reimplement a number of
57 // functions that they may use to indicate to the scheduler changes in the state
58 // for any tasks they manage. They may be used to reprioritize or suspend tasks
59 // under their control, and will also be notified before and after tasks under
60 // their control are executed. Their methods will only be called once per event
61 // loop turn, however they may still incur some performance overhead. In
62 // addition to this frequent reprioritizations may incur a significant
63 // performance overhead and are discouraged. A TaskManager may currently only be
64 // used to manage tasks that are bound to the Gecko Main Thread.
65 class TaskManager {
66 public:
67 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TaskManager)
69 TaskManager() : mTaskCount(0) {}
71 // Subclasses implementing task manager will have this function called to
72 // determine whether their associated tasks are currently suspended. This
73 // will only be called once per iteration of the task queue, this means that
74 // suspension of tasks managed by a single TaskManager may be assumed to
75 // occur atomically.
76 virtual bool IsSuspended(const MutexAutoLock& aProofOfLock) { return false; }
78 // Subclasses may implement this in order to supply a priority adjustment
79 // to their managed tasks. This is called once per iteration of the task
80 // queue, and may be assumed to occur atomically for all managed tasks.
81 virtual int32_t GetPriorityModifierForEventLoopTurn(
82 const MutexAutoLock& aProofOfLock) {
83 return 0;
86 void DidQueueTask() { ++mTaskCount; }
87 // This is called when a managed task is about to be executed by the
88 // scheduler. Anyone reimplementing this should ensure to call the parent or
89 // decrement mTaskCount.
90 virtual void WillRunTask() { --mTaskCount; }
91 // This is called when a managed task has finished being executed by the
92 // scheduler.
93 virtual void DidRunTask() {}
94 uint32_t PendingTaskCount() { return mTaskCount; }
96 protected:
97 virtual ~TaskManager() {}
99 private:
100 friend class TaskController;
102 enum class IterationType { NOT_EVENT_LOOP_TURN, EVENT_LOOP_TURN };
103 bool UpdateCachesForCurrentIterationAndReportPriorityModifierChanged(
104 const MutexAutoLock& aProofOfLock, IterationType aIterationType);
106 bool mCurrentSuspended = false;
107 int32_t mCurrentPriorityModifier = 0;
109 std::atomic<uint32_t> mTaskCount;
112 // A Task is the the base class for any unit of work that may be scheduled.
113 // Subclasses may specify their priority and whether they should be bound to
114 // the Gecko Main thread. When not bound to the main thread tasks may be
115 // executed on any available thread (including the main thread), but they may
116 // also be executed in parallel to any other task they do not have a dependency
117 // relationship with. Tasks will be run in order of object creation.
118 class Task {
119 public:
120 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Task)
122 bool IsMainThreadOnly() { return mMainThreadOnly; }
124 // This returns the current task priority with its modifier applied.
125 uint32_t GetPriority() { return mPriority + mPriorityModifier; }
126 uint64_t GetSeqNo() { return mSeqNo; }
128 // Callee needs to assume this may be called on any thread.
129 // aInterruptPriority passes the priority of the higher priority task that
130 // is ready to be executed. The task may safely ignore this function, or
131 // interrupt any work being done. It may return 'false' from its run function
132 // in order to be run automatically in the future, or true if it will
133 // reschedule incomplete work manually.
134 virtual void RequestInterrupt(uint32_t aInterruptPriority) {}
136 // At the moment this -must- be called before the task is added to the
137 // controller. Calling this after tasks have been added to the controller
138 // results in undefined behavior!
139 // At submission, tasks must depend only on tasks managed by the same, or
140 // no idle manager.
141 void AddDependency(Task* aTask) {
142 MOZ_ASSERT(aTask);
143 MOZ_ASSERT(!mIsInGraph);
144 mDependencies.insert(aTask);
147 // This sets the TaskManager for the current task. Calling this after the
148 // task has been added to the TaskController results in undefined behavior.
149 void SetManager(TaskManager* aManager) {
150 MOZ_ASSERT(mMainThreadOnly);
151 MOZ_ASSERT(!mIsInGraph);
152 mTaskManager = aManager;
154 TaskManager* GetManager() { return mTaskManager; }
156 struct PriorityCompare {
157 bool operator()(const RefPtr<Task>& aTaskA,
158 const RefPtr<Task>& aTaskB) const {
159 uint32_t prioA = aTaskA->GetPriority();
160 uint32_t prioB = aTaskB->GetPriority();
161 return (prioA > prioB) ||
162 (prioA == prioB && (aTaskA->GetSeqNo() < aTaskB->GetSeqNo()));
166 // Tell the task about its idle deadline. Will only be called for
167 // tasks managed by an IdleTaskManager, right before the task runs.
168 virtual void SetIdleDeadline(TimeStamp aDeadline) {}
170 virtual PerformanceCounter* GetPerformanceCounter() const { return nullptr; }
172 // Get a name for this task. This returns false if the task has no name.
173 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
174 virtual bool GetName(nsACString& aName) = 0;
175 #else
176 virtual bool GetName(nsACString& aName) { return false; }
177 #endif
179 protected:
180 Task(bool aMainThreadOnly,
181 uint32_t aPriority = static_cast<uint32_t>(kDefaultPriorityValue))
182 : mMainThreadOnly(aMainThreadOnly),
183 mSeqNo(sCurrentTaskSeqNo++),
184 mPriority(aPriority) {}
186 Task(bool aMainThreadOnly,
187 EventQueuePriority aPriority = kDefaultPriorityValue)
188 : mMainThreadOnly(aMainThreadOnly),
189 mSeqNo(sCurrentTaskSeqNo++),
190 mPriority(static_cast<uint32_t>(aPriority)) {}
192 virtual ~Task() {}
194 friend class TaskController;
196 // When this returns false, the task is considered incomplete and will be
197 // rescheduled at the current 'mPriority' level.
198 virtual bool Run() = 0;
200 private:
201 Task* GetHighestPriorityDependency();
203 // Iterator pointing to this task's position in
204 // mThreadableTasks/mMainThreadTasks if, and only if this task is currently
205 // scheduled to be executed. This allows fast access to the task's position
206 // in the set, allowing for fast removal.
207 // This is safe, and remains valid unless the task is removed from the set.
208 // See also iterator invalidation in:
209 // https://en.cppreference.com/w/cpp/container
211 // Or the spec:
212 // "All Associative Containers: The insert and emplace members shall not
213 // affect the validity of iterators and references to the container
214 // [26.2.6/9]" "All Associative Containers: The erase members shall invalidate
215 // only iterators and references to the erased elements [26.2.6/9]"
216 std::set<RefPtr<Task>, PriorityCompare>::iterator mIterator;
217 std::set<RefPtr<Task>, PriorityCompare> mDependencies;
219 RefPtr<TaskManager> mTaskManager;
221 // Access to these variables is protected by the GraphMutex.
222 bool mMainThreadOnly;
223 bool mCompleted = false;
224 bool mInProgress = false;
225 #ifdef DEBUG
226 bool mIsInGraph = false;
227 #endif
229 static std::atomic<uint64_t> sCurrentTaskSeqNo;
230 int64_t mSeqNo;
231 uint32_t mPriority;
232 // Modifier currently being applied to this task by its taskmanager.
233 int32_t mPriorityModifier = 0;
234 // Time this task was inserted into the task graph, this is used by the
235 // profiler.
236 mozilla::TimeStamp mInsertionTime;
239 struct PoolThread {
240 PRThread* mThread;
241 RefPtr<Task> mCurrentTask;
242 // This may be higher than mCurrentTask's priority due to priority
243 // propagation. This is -only- valid when mCurrentTask != nullptr.
244 uint32_t mEffectiveTaskPriority;
247 // A task manager implementation for priority levels that should only
248 // run during idle periods.
249 class IdleTaskManager : public TaskManager {
250 public:
251 explicit IdleTaskManager(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
252 : mIdlePeriodState(std::move(aIdlePeriod)) {}
254 IdlePeriodState& State() { return mIdlePeriodState; }
256 bool IsSuspended(const MutexAutoLock& aProofOfLock) override {
257 TimeStamp idleDeadline = State().GetCachedIdleDeadline();
258 return !idleDeadline;
261 private:
262 // Tracking of our idle state of various sorts.
263 IdlePeriodState mIdlePeriodState;
266 // The TaskController is the core class of the scheduler. It is used to
267 // schedule tasks to be executed, as well as to reprioritize tasks that have
268 // already been scheduled. The core functions to do this are AddTask and
269 // ReprioritizeTask.
270 class TaskController {
271 public:
272 TaskController()
273 : mGraphMutex("TaskController::mGraphMutex"),
274 mThreadPoolCV(mGraphMutex, "TaskController::mThreadPoolCV"),
275 mMainThreadCV(mGraphMutex, "TaskController::mMainThreadCV") {}
277 static TaskController* Get();
279 static bool Initialize();
281 void SetThreadObserver(nsIThreadObserver* aObserver) {
282 MutexAutoLock lock(mGraphMutex);
283 mObserver = aObserver;
285 void SetConditionVariable(CondVar* aExternalCondVar) {
286 mExternalCondVar = aExternalCondVar;
289 void SetIdleTaskManager(IdleTaskManager* aIdleTaskManager) {
290 mIdleTaskManager = aIdleTaskManager;
292 IdleTaskManager* GetIdleTaskManager() { return mIdleTaskManager.get(); }
294 // Initialization and shutdown code.
295 void SetPerformanceCounterState(
296 PerformanceCounterState* aPerformanceCounterState);
298 static void Shutdown();
300 // This adds a task to the TaskController graph.
301 // This may be called on any thread.
302 void AddTask(already_AddRefed<Task>&& aTask);
304 // This wait function is the theoretical function you would need if our main
305 // thread needs to also process OS messages or something along those lines.
306 void WaitForTaskOrMessage();
308 // This gets the next (highest priority) task that is only allowed to execute
309 // on the main thread.
310 void ExecuteNextTaskOnlyMainThread();
312 // Process all pending main thread tasks.
313 void ProcessPendingMTTask(bool aMayWait = false);
315 // This allows reprioritization of a task already in the task graph.
316 // This may be called on any thread.
317 void ReprioritizeTask(Task* aTask, uint32_t aPriority);
319 void DispatchRunnable(already_AddRefed<nsIRunnable>&& aRunnable,
320 uint32_t aPriority, TaskManager* aManager = nullptr);
322 nsIRunnable* GetRunnableForMTTask(bool aReallyWait);
324 bool HasMainThreadPendingTasks();
326 // Let users know whether the last main thread task runnable did work.
327 bool MTTaskRunnableProcessedTask() { return mMTTaskRunnableProcessedTask; }
329 static int32_t GetPoolThreadCount();
330 static size_t GetThreadStackSize();
332 private:
333 friend void ThreadFuncPoolThread(void* aIndex);
335 bool InitializeInternal();
337 void InitializeThreadPool();
339 // This gets the next (highest priority) task that is only allowed to execute
340 // on the main thread, if any, and executes it.
341 // Returns true if it succeeded.
342 bool ExecuteNextTaskOnlyMainThreadInternal(const MutexAutoLock& aProofOfLock);
344 // The guts of ExecuteNextTaskOnlyMainThreadInternal, which get idle handling
345 // wrapped around them. Returns whether a task actually ran.
346 bool DoExecuteNextTaskOnlyMainThreadInternal(
347 const MutexAutoLock& aProofOfLock);
349 Task* GetFinalDependency(Task* aTask);
350 void MaybeInterruptTask(Task* aTask);
351 Task* GetHighestPriorityMTTask();
353 void EnsureMainThreadTasksScheduled();
355 void ProcessUpdatedPriorityModifier(TaskManager* aManager);
357 void ShutdownThreadPoolInternal();
358 void ShutdownInternal();
360 void RunPoolThread();
362 static std::unique_ptr<TaskController> sSingleton;
363 static StaticMutex sSingletonMutex;
365 // This protects access to the task graph.
366 Mutex mGraphMutex;
368 // This protects thread pool initialization. We cannot do this from within
369 // the GraphMutex, since thread creation on Windows can generate events on
370 // the main thread that need to be handled.
371 Mutex mPoolInitializationMutex =
372 Mutex("TaskController::mPoolInitializationMutex");
374 CondVar mThreadPoolCV;
375 CondVar mMainThreadCV;
377 // Variables below are protected by mGraphMutex.
379 std::vector<PoolThread> mPoolThreads;
380 std::stack<RefPtr<Task>> mCurrentTasksMT;
382 // A list of all tasks ordered by priority.
383 std::set<RefPtr<Task>, Task::PriorityCompare> mThreadableTasks;
384 std::set<RefPtr<Task>, Task::PriorityCompare> mMainThreadTasks;
386 // TaskManagers currently active.
387 // We can use a raw pointer since tasks always hold on to their TaskManager.
388 std::set<TaskManager*> mTaskManagers;
390 // This ensures we keep running the main thread if we processed a task there.
391 bool mMayHaveMainThreadTask = true;
392 bool mShuttingDown = false;
394 // This stores whether the last main thread task runnable did work.
395 bool mMTTaskRunnableProcessedTask = false;
397 // Whether our thread pool is initialized. We use this currently to avoid
398 // starting the threads in processes where it's never used. This is protected
399 // by mPoolInitializationMutex.
400 bool mThreadPoolInitialized = false;
402 // Whether we have scheduled a runnable on the main thread event loop.
403 // This is used for nsIRunnable compatibility.
404 RefPtr<nsIRunnable> mMTProcessingRunnable;
405 RefPtr<nsIRunnable> mMTBlockingProcessingRunnable;
407 // XXX - Thread observer to notify when a new event has been dispatched
408 nsIThreadObserver* mObserver = nullptr;
409 // XXX - External condvar to notify when we have received an event
410 CondVar* mExternalCondVar = nullptr;
411 // Idle task manager so we can properly do idle state stuff.
412 RefPtr<IdleTaskManager> mIdleTaskManager;
414 // Our tracking of our performance counter and long task state,
415 // shared with nsThread.
416 PerformanceCounterState* mPerformanceCounterState = nullptr;
419 } // namespace mozilla
421 #endif // mozilla_TaskController_h