1 // Copyright 2015 The Chromium Authors 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_WORK_QUEUE_H_ 6 #define BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_ 7 8 #include <optional> 9 10 #include "base/base_export.h" 11 #include "base/containers/intrusive_heap.h" 12 #include "base/memory/raw_ptr.h" 13 #include "base/task/sequence_manager/fence.h" 14 #include "base/task/sequence_manager/sequenced_task_source.h" 15 #include "base/task/sequence_manager/task_queue_impl.h" 16 #include "base/values.h" 17 18 namespace base { 19 namespace sequence_manager { 20 class TaskOrder; 21 22 namespace internal { 23 24 class WorkQueueSets; 25 26 // This class keeps track of immediate and delayed tasks which are due to run 27 // now. It interfaces deeply with WorkQueueSets which keeps track of which queue 28 // (with a given priority) contains the oldest task. 29 // 30 // If a fence is inserted, WorkQueue behaves normally up until 31 // TakeTaskFromWorkQueue reaches or exceeds the fence. At that point it the 32 // API subset used by WorkQueueSets pretends the WorkQueue is empty until the 33 // fence is removed. This functionality is a primitive intended for use by 34 // throttling mechanisms. 35 class BASE_EXPORT WorkQueue { 36 public: 37 using QueueType = internal::TaskQueueImpl::WorkQueueType; 38 39 // Note |task_queue| can be null if queue_type is kNonNestable. 40 WorkQueue(TaskQueueImpl* task_queue, const char* name, QueueType queue_type); 41 WorkQueue(const WorkQueue&) = delete; 42 WorkQueue& operator=(const WorkQueue&) = delete; 43 ~WorkQueue(); 44 45 // Associates this work queue with the given work queue sets. This must be 46 // called before any tasks can be inserted into this work queue. 47 void AssignToWorkQueueSets(WorkQueueSets* work_queue_sets); 48 49 // Assigns the current set index. 50 void AssignSetIndex(size_t work_queue_set_index); 51 52 Value::List AsValue(TimeTicks now) const; 53 54 // Returns true if the |tasks_| is empty. This method ignores any fences. Empty()55 bool Empty() const { return tasks_.empty(); } 56 57 // Returns the front task's TaskOrder if `tasks_` is non-empty and a fence 58 // hasn't been reached, otherwise returns nullopt. 59 std::optional<TaskOrder> GetFrontTaskOrder() const; 60 61 // Returns the first task in this queue or null if the queue is empty. This 62 // method ignores any fences. 63 const Task* GetFrontTask() const; 64 65 // Returns the last task in this queue or null if the queue is empty. This 66 // method ignores any fences. 67 const Task* GetBackTask() const; 68 69 // Pushes the task onto the |tasks_| and if a fence hasn't been reached 70 // it informs the WorkQueueSets if the head changed. 71 void Push(Task task); 72 73 // RAII helper that helps efficiently push N Tasks to a WorkQueue. 74 class BASE_EXPORT TaskPusher { 75 public: 76 TaskPusher(const TaskPusher&) = delete; 77 TaskPusher(TaskPusher&& other); 78 ~TaskPusher(); 79 80 void Push(Task task); 81 82 private: 83 friend class WorkQueue; 84 85 explicit TaskPusher(WorkQueue* work_queue); 86 87 // `work_queue_` is not a raw_ptr<...> for performance reasons (based on 88 // analysis of sampling profiler data and tab_search:top100:2020). 89 RAW_PTR_EXCLUSION WorkQueue* work_queue_; 90 91 const bool was_empty_; 92 }; 93 94 // Returns an RAII helper to efficiently push multiple tasks. 95 TaskPusher CreateTaskPusher(); 96 97 // Pushes the task onto the front of the |tasks_| and if it's before any 98 // fence it informs the WorkQueueSets the head changed. Use with caution this 99 // API can easily lead to task starvation if misused. 100 void PushNonNestableTaskToFront(Task task); 101 102 // Reloads the empty |tasks_| with 103 // |task_queue_->TakeImmediateIncomingQueue| and if a fence hasn't been 104 // reached it informs the WorkQueueSets if the head changed. 105 void TakeImmediateIncomingQueueTasks(); 106 Size()107 size_t Size() const { return tasks_.size(); } 108 Capacity()109 size_t Capacity() const { return tasks_.capacity(); } 110 111 // Pulls a task off the |tasks_| and informs the WorkQueueSets. If the 112 // task removed had an enqueue order >= the current fence then WorkQueue 113 // pretends to be empty as far as the WorkQueueSets is concerned. 114 Task TakeTaskFromWorkQueue(); 115 116 // Removes all canceled tasks from the head of the list. Returns true if any 117 // tasks were removed. 118 bool RemoveAllCanceledTasksFromFront(); 119 name()120 const char* name() const { return name_; } 121 task_queue()122 TaskQueueImpl* task_queue() const { return task_queue_; } 123 work_queue_sets()124 WorkQueueSets* work_queue_sets() const { return work_queue_sets_; } 125 work_queue_set_index()126 size_t work_queue_set_index() const { return work_queue_set_index_; } 127 heap_handle()128 HeapHandle heap_handle() const { return heap_handle_; } 129 set_heap_handle(HeapHandle handle)130 void set_heap_handle(HeapHandle handle) { heap_handle_ = handle; } 131 queue_type()132 QueueType queue_type() const { return queue_type_; } 133 134 // Submit a fence. When TakeTaskFromWorkQueue encounters a task whose 135 // enqueue_order is >= |fence| then the WorkQueue will start pretending to be. 136 // empty. 137 // Inserting a fence may supersede a previous one and unblock some tasks. 138 // Returns true if any tasks where unblocked, returns false otherwise. 139 bool InsertFence(Fence fence); 140 141 // Submit a fence without triggering a WorkQueueSets notification. 142 // Caller must ensure that WorkQueueSets are properly updated. 143 // This method should not be called when a fence is already present. 144 void InsertFenceSilently(Fence fence); 145 146 // Removes any fences that where added and if WorkQueue was pretending to be 147 // empty, then the real value is reported to WorkQueueSets. Returns true if 148 // any tasks where unblocked. 149 bool RemoveFence(); 150 151 // Returns true if any tasks are blocked by the fence. Returns true if the 152 // queue is empty and fence has been set (i.e. future tasks would be blocked). 153 // Otherwise returns false. 154 bool BlockedByFence() const; 155 156 // Shrinks |tasks_| if it's wasting memory. 157 void MaybeShrinkQueue(); 158 159 // Test support function. This should not be used in production code. 160 void PopTaskForTesting(); 161 162 // Iterates through |tasks_| adding any that are older than |reference| to 163 // |result|. 164 void CollectTasksOlderThan(TaskOrder reference, 165 std::vector<const Task*>* result) const; 166 167 bool InsertFenceImpl(Fence fence); 168 169 TaskQueueImpl::TaskDeque tasks_; 170 raw_ptr<WorkQueueSets> work_queue_sets_ = nullptr; // NOT OWNED. 171 const raw_ptr<TaskQueueImpl> task_queue_; // NOT OWNED. 172 size_t work_queue_set_index_ = 0; 173 174 // Iff the queue isn't empty (or appearing to be empty due to a fence) then 175 // |heap_handle_| will be valid and correspond to this queue's location within 176 // an IntrusiveHeap inside the WorkQueueSet. 177 HeapHandle heap_handle_; 178 const char* const name_; 179 std::optional<Fence> fence_; 180 const QueueType queue_type_; 181 }; 182 183 } // namespace internal 184 } // namespace sequence_manager 185 } // namespace base 186 187 #endif // BASE_TASK_SEQUENCE_MANAGER_WORK_QUEUE_H_ 188