xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/work_queue.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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