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