1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_ 6 #define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_ 7 8 #include <memory> 9 10 #include "base/macros.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/optional.h" 13 #include "base/single_thread_task_runner.h" 14 #include "base/task/common/checked_lock.h" 15 #include "base/task/sequence_manager/lazy_now.h" 16 #include "base/task/sequence_manager/tasks.h" 17 #include "base/task/task_observer.h" 18 #include "base/threading/platform_thread.h" 19 #include "base/time/time.h" 20 21 namespace base { 22 23 class TaskObserver; 24 25 namespace trace_event { 26 class BlameContext; 27 } 28 29 namespace sequence_manager { 30 31 namespace internal { 32 class AssociatedThreadId; 33 class SequenceManagerImpl; 34 class TaskQueueImpl; 35 } // namespace internal 36 37 class TimeDomain; 38 39 // TODO(kraynov): Make TaskQueue to actually be an interface for TaskQueueImpl 40 // and stop using ref-counting because we're no longer tied to task runner 41 // lifecycle and there's no other need for ref-counting either. 42 // NOTE: When TaskQueue gets automatically deleted on zero ref-count, 43 // TaskQueueImpl gets gracefully shutdown. It means that it doesn't get 44 // unregistered immediately and might accept some last minute tasks until 45 // SequenceManager will unregister it at some point. It's done to ensure that 46 // task queue always gets unregistered on the main thread. 47 class BASE_EXPORT TaskQueue : public RefCountedThreadSafe<TaskQueue> { 48 public: 49 class Observer { 50 public: 51 virtual ~Observer() = default; 52 53 // Notify observer that the time at which this queue wants to run 54 // the next task has changed. |next_wakeup| can be in the past 55 // (e.g. TimeTicks() can be used to notify about immediate work). 56 // Can be called on any thread 57 // All methods but SetObserver, SetTimeDomain and GetTimeDomain can be 58 // called on |queue|. 59 // 60 // TODO(altimin): Make it Optional<TimeTicks> to tell 61 // observer about cancellations. 62 virtual void OnQueueNextWakeUpChanged(TimeTicks next_wake_up) = 0; 63 }; 64 65 // Shuts down the queue. All tasks currently queued will be discarded. 66 virtual void ShutdownTaskQueue(); 67 68 // Shuts down the queue when there are no more tasks queued. 69 void ShutdownTaskQueueGracefully(); 70 71 // TODO(scheduler-dev): Could we define a more clear list of priorities? 72 // See https://crbug.com/847858. 73 enum QueuePriority : uint8_t { 74 // Queues with control priority will run before any other queue, and will 75 // explicitly starve other queues. Typically this should only be used for 76 // private queues which perform control operations. 77 kControlPriority = 0, 78 79 // The selector will prioritize highest over high, normal and low; and 80 // high over normal and low; and normal over low. However it will ensure 81 // neither of the lower priority queues can be completely starved by higher 82 // priority tasks. All three of these queues will always take priority over 83 // and can starve the best effort queue. 84 kHighestPriority = 1, 85 86 kVeryHighPriority = 2, 87 88 kHighPriority = 3, 89 90 // Queues with normal priority are the default. 91 kNormalPriority = 4, 92 kLowPriority = 5, 93 94 // Queues with best effort priority will only be run if all other queues are 95 // empty. They can be starved by the other queues. 96 kBestEffortPriority = 6, 97 // Must be the last entry. 98 kQueuePriorityCount = 7, 99 kFirstQueuePriority = kControlPriority, 100 }; 101 102 // Can be called on any thread. 103 static const char* PriorityToString(QueuePriority priority); 104 105 // Options for constructing a TaskQueue. 106 struct Spec { SpecSpec107 explicit Spec(const char* name) : name(name) {} 108 SetShouldMonitorQuiescenceSpec109 Spec SetShouldMonitorQuiescence(bool should_monitor) { 110 should_monitor_quiescence = should_monitor; 111 return *this; 112 } 113 SetShouldNotifyObserversSpec114 Spec SetShouldNotifyObservers(bool run_observers) { 115 should_notify_observers = run_observers; 116 return *this; 117 } 118 119 // Delayed fences require Now() to be sampled when posting immediate tasks 120 // which is not free. SetDelayedFencesAllowedSpec121 Spec SetDelayedFencesAllowed(bool allow_delayed_fences) { 122 delayed_fence_allowed = allow_delayed_fences; 123 return *this; 124 } 125 SetTimeDomainSpec126 Spec SetTimeDomain(TimeDomain* domain) { 127 time_domain = domain; 128 return *this; 129 } 130 131 const char* name; 132 bool should_monitor_quiescence = false; 133 TimeDomain* time_domain = nullptr; 134 bool should_notify_observers = true; 135 bool delayed_fence_allowed = false; 136 }; 137 138 // TODO(altimin): Make this private after TaskQueue/TaskQueueImpl refactoring. 139 TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl, 140 const TaskQueue::Spec& spec); 141 142 // Information about task execution. 143 // 144 // Wall-time related methods (start_time, end_time, wall_duration) can be 145 // called only when |has_wall_time()| is true. 146 // Thread-time related mehtods (start_thread_time, end_thread_time, 147 // thread_duration) can be called only when |has_thread_time()| is true. 148 // 149 // start_* should be called after RecordTaskStart. 150 // end_* and *_duration should be called after RecordTaskEnd. 151 class BASE_EXPORT TaskTiming { 152 public: 153 enum class State { NotStarted, Running, Finished }; 154 enum class TimeRecordingPolicy { DoRecord, DoNotRecord }; 155 156 TaskTiming(bool has_wall_time, bool has_thread_time); 157 has_wall_time()158 bool has_wall_time() const { return has_wall_time_; } has_thread_time()159 bool has_thread_time() const { return has_thread_time_; } 160 start_time()161 base::TimeTicks start_time() const { 162 DCHECK(has_wall_time()); 163 return start_time_; 164 } end_time()165 base::TimeTicks end_time() const { 166 DCHECK(has_wall_time()); 167 return end_time_; 168 } wall_duration()169 base::TimeDelta wall_duration() const { 170 DCHECK(has_wall_time()); 171 return end_time_ - start_time_; 172 } start_thread_time()173 base::ThreadTicks start_thread_time() const { 174 DCHECK(has_thread_time()); 175 return start_thread_time_; 176 } end_thread_time()177 base::ThreadTicks end_thread_time() const { 178 DCHECK(has_thread_time()); 179 return end_thread_time_; 180 } thread_duration()181 base::TimeDelta thread_duration() const { 182 DCHECK(has_thread_time()); 183 return end_thread_time_ - start_thread_time_; 184 } 185 state()186 State state() const { return state_; } 187 188 void RecordTaskStart(LazyNow* now); 189 void RecordTaskEnd(LazyNow* now); 190 191 // Protected for tests. 192 protected: 193 State state_ = State::NotStarted; 194 195 bool has_wall_time_; 196 bool has_thread_time_; 197 198 base::TimeTicks start_time_; 199 base::TimeTicks end_time_; 200 base::ThreadTicks start_thread_time_; 201 base::ThreadTicks end_thread_time_; 202 }; 203 204 // An interface that lets the owner vote on whether or not the associated 205 // TaskQueue should be enabled. 206 class BASE_EXPORT QueueEnabledVoter { 207 public: 208 ~QueueEnabledVoter(); 209 210 QueueEnabledVoter(const QueueEnabledVoter&) = delete; 211 const QueueEnabledVoter& operator=(const QueueEnabledVoter&) = delete; 212 213 // Votes to enable or disable the associated TaskQueue. The TaskQueue will 214 // only be enabled if all the voters agree it should be enabled, or if there 215 // are no voters. 216 // NOTE this must be called on the thread the associated TaskQueue was 217 // created on. 218 void SetVoteToEnable(bool enabled); 219 IsVotingToEnable()220 bool IsVotingToEnable() const { return enabled_; } 221 222 private: 223 friend class TaskQueue; 224 explicit QueueEnabledVoter(scoped_refptr<TaskQueue> task_queue); 225 226 scoped_refptr<TaskQueue> const task_queue_; 227 bool enabled_; 228 }; 229 230 // Returns an interface that allows the caller to vote on whether or not this 231 // TaskQueue is enabled. The TaskQueue will be enabled if there are no voters 232 // or if all agree it should be enabled. 233 // NOTE this must be called on the thread this TaskQueue was created by. 234 std::unique_ptr<QueueEnabledVoter> CreateQueueEnabledVoter(); 235 236 // NOTE this must be called on the thread this TaskQueue was created by. 237 bool IsQueueEnabled() const; 238 239 // Returns true if the queue is completely empty. 240 bool IsEmpty() const; 241 242 // Returns the number of pending tasks in the queue. 243 size_t GetNumberOfPendingTasks() const; 244 245 // Returns true if the queue has work that's ready to execute now. 246 // NOTE: this must be called on the thread this TaskQueue was created by. 247 bool HasTaskToRunImmediately() const; 248 249 // Returns requested run time of next scheduled wake-up for a delayed task 250 // which is not ready to run. If there are no such tasks (immediate tasks 251 // don't count) or the queue is disabled it returns nullopt. 252 // NOTE: this must be called on the thread this TaskQueue was created by. 253 Optional<TimeTicks> GetNextScheduledWakeUp(); 254 255 // Can be called on any thread. 256 virtual const char* GetName() const; 257 258 // Set the priority of the queue to |priority|. NOTE this must be called on 259 // the thread this TaskQueue was created by. 260 void SetQueuePriority(QueuePriority priority); 261 262 // Returns the current queue priority. 263 QueuePriority GetQueuePriority() const; 264 265 // These functions can only be called on the same thread that the task queue 266 // manager executes its tasks on. 267 void AddTaskObserver(TaskObserver* task_observer); 268 void RemoveTaskObserver(TaskObserver* task_observer); 269 270 // Set the blame context which is entered and left while executing tasks from 271 // this task queue. |blame_context| must be null or outlive this task queue. 272 // Must be called on the thread this TaskQueue was created by. 273 void SetBlameContext(trace_event::BlameContext* blame_context); 274 275 // Removes the task queue from the previous TimeDomain and adds it to 276 // |domain|. This is a moderately expensive operation. 277 void SetTimeDomain(TimeDomain* domain); 278 279 // Returns the queue's current TimeDomain. Can be called from any thread. 280 TimeDomain* GetTimeDomain() const; 281 282 enum class InsertFencePosition { 283 kNow, // Tasks posted on the queue up till this point further may run. 284 // All further tasks are blocked. 285 kBeginningOfTime, // No tasks posted on this queue may run. 286 }; 287 288 // Inserts a barrier into the task queue which prevents tasks with an enqueue 289 // order greater than the fence from running until either the fence has been 290 // removed or a subsequent fence has unblocked some tasks within the queue. 291 // Note: delayed tasks get their enqueue order set once their delay has 292 // expired, and non-delayed tasks get their enqueue order set when posted. 293 // 294 // Fences come in three flavours: 295 // - Regular (InsertFence(NOW)) - all tasks posted after this moment 296 // are blocked. 297 // - Fully blocking (InsertFence(kBeginningOfTime)) - all tasks including 298 // already posted are blocked. 299 // - Delayed (InsertFenceAt(timestamp)) - blocks all tasks posted after given 300 // point in time (must be in the future). 301 // 302 // Only one fence can be scheduled at a time. Inserting a new fence 303 // will automatically remove the previous one, regardless of fence type. 304 void InsertFence(InsertFencePosition position); 305 306 // Delayed fences are only allowed for queues created with 307 // SetDelayedFencesAllowed(true) because this feature implies sampling Now() 308 // (which isn't free) for every PostTask, even those with zero delay. 309 void InsertFenceAt(TimeTicks time); 310 311 // Removes any previously added fence and unblocks execution of any tasks 312 // blocked by it. 313 void RemoveFence(); 314 315 // Returns true if the queue has a fence but it isn't necessarily blocking 316 // execution of tasks (it may be the case if tasks enqueue order hasn't 317 // reached the number set for a fence). 318 bool HasActiveFence(); 319 320 // Returns true if the queue has a fence which is blocking execution of tasks. 321 bool BlockedByFence() const; 322 323 // Returns an EnqueueOrder generated at the last transition to unblocked. A 324 // queue is unblocked when it is enabled and no fence prevents the front task 325 // from running. If the EnqueueOrder of a task is greater than this when it 326 // starts running, it means that is was never blocked. 327 EnqueueOrder GetEnqueueOrderAtWhichWeBecameUnblocked() const; 328 329 void SetObserver(Observer* observer); 330 331 // Controls whether or not the queue will emit traces events when tasks are 332 // posted to it while disabled. This only applies for the current or next 333 // period during which the queue is disabled. When the queue is re-enabled 334 // this will revert back to the default value of false. 335 void SetShouldReportPostedTasksWhenDisabled(bool should_report); 336 337 // Create a task runner for this TaskQueue which will annotate all 338 // posted tasks with the given task type. 339 // May be called on any thread. 340 // NOTE: Task runners don't hold a reference to a TaskQueue, hence, 341 // it's required to retain that reference to prevent automatic graceful 342 // shutdown. Unique ownership of task queues will fix this issue soon. 343 scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner(TaskType task_type); 344 345 // Default task runner which doesn't annotate tasks with a task type. task_runner()346 scoped_refptr<SingleThreadTaskRunner> task_runner() const { 347 return default_task_runner_; 348 } 349 350 protected: 351 virtual ~TaskQueue(); 352 GetTaskQueueImpl()353 internal::TaskQueueImpl* GetTaskQueueImpl() const { return impl_.get(); } 354 355 private: 356 friend class RefCountedThreadSafe<TaskQueue>; 357 friend class internal::SequenceManagerImpl; 358 friend class internal::TaskQueueImpl; 359 360 void AddQueueEnabledVoter(bool voter_is_enabled); 361 void RemoveQueueEnabledVoter(bool voter_is_enabled); 362 bool AreAllQueueEnabledVotersEnabled() const; 363 void OnQueueEnabledVoteChanged(bool enabled); 364 365 bool IsOnMainThread() const; 366 367 // TaskQueue has ownership of an underlying implementation but in certain 368 // cases (e.g. detached frames) their lifetime may diverge. 369 // This method should be used to take away the impl for graceful shutdown. 370 // TaskQueue will disregard any calls or posting tasks thereafter. 371 std::unique_ptr<internal::TaskQueueImpl> TakeTaskQueueImpl(); 372 373 // |impl_| can be written to on the main thread but can be read from 374 // any thread. 375 // |impl_lock_| must be acquired when writing to |impl_| or when accessing 376 // it from non-main thread. Reading from the main thread does not require 377 // a lock. 378 mutable base::internal::CheckedLock impl_lock_{ 379 base::internal::UniversalPredecessor{}}; 380 std::unique_ptr<internal::TaskQueueImpl> impl_; 381 382 const WeakPtr<internal::SequenceManagerImpl> sequence_manager_; 383 384 scoped_refptr<internal::AssociatedThreadId> associated_thread_; 385 scoped_refptr<SingleThreadTaskRunner> default_task_runner_; 386 387 int enabled_voter_count_ = 0; 388 int voter_count_ = 0; 389 const char* name_; 390 391 DISALLOW_COPY_AND_ASSIGN(TaskQueue); 392 }; 393 394 } // namespace sequence_manager 395 } // namespace base 396 397 #endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_ 398