xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/wake_up_queue.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 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 #include "base/task/sequence_manager/wake_up_queue.h"
6 
7 #include <optional>
8 
9 #include "base/task/sequence_manager/associated_thread_id.h"
10 #include "base/task/sequence_manager/sequence_manager_impl.h"
11 #include "base/task/sequence_manager/task_queue_impl.h"
12 #include "base/threading/thread_checker.h"
13 
14 namespace base {
15 namespace sequence_manager {
16 namespace internal {
17 
WakeUpQueue(scoped_refptr<const internal::AssociatedThreadId> associated_thread)18 WakeUpQueue::WakeUpQueue(
19     scoped_refptr<const internal::AssociatedThreadId> associated_thread)
20     : associated_thread_(std::move(associated_thread)) {}
21 
~WakeUpQueue()22 WakeUpQueue::~WakeUpQueue() {
23   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
24 }
25 
RemoveAllCanceledDelayedTasksFromFront(LazyNow * lazy_now)26 void WakeUpQueue::RemoveAllCanceledDelayedTasksFromFront(LazyNow* lazy_now) {
27   // Repeatedly trim the front of the top queue until it stabilizes. This is
28   // needed because a different queue can become the top one once you remove the
29   // canceled tasks.
30   while (!wake_up_queue_.empty()) {
31     auto* top_queue = wake_up_queue_.top().queue.get();
32 
33     // If no tasks are removed from the top queue, then it means the top queue
34     // cannot change anymore.
35     if (!top_queue->RemoveAllCanceledDelayedTasksFromFront(lazy_now))
36       break;
37   }
38 }
39 
40 // TODO(kraynov): https://crbug.com/857101 Consider making an interface
41 // for SequenceManagerImpl which will expose SetNextDelayedDoWork and
42 // MaybeScheduleImmediateWork methods to make the functions below pure-virtual.
43 
SetNextWakeUpForQueue(internal::TaskQueueImpl * queue,LazyNow * lazy_now,std::optional<WakeUp> wake_up)44 void WakeUpQueue::SetNextWakeUpForQueue(internal::TaskQueueImpl* queue,
45                                         LazyNow* lazy_now,
46                                         std::optional<WakeUp> wake_up) {
47   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
48   DCHECK_EQ(queue->wake_up_queue(), this);
49   DCHECK(queue->IsQueueEnabled() || !wake_up);
50 
51   std::optional<WakeUp> previous_wake_up = GetNextDelayedWakeUp();
52   std::optional<WakeUpResolution> previous_queue_resolution;
53   if (queue->heap_handle().IsValid()) {
54     previous_queue_resolution =
55         wake_up_queue_.at(queue->heap_handle()).wake_up.resolution;
56   }
57 
58   if (wake_up) {
59     // Insert a new wake-up into the heap.
60     if (queue->heap_handle().IsValid()) {
61       // O(log n)
62       wake_up_queue_.Replace(queue->heap_handle(), {wake_up.value(), queue});
63     } else {
64       // O(log n)
65       wake_up_queue_.insert({wake_up.value(), queue});
66     }
67   } else {
68     // Remove a wake-up from heap if present.
69     if (queue->heap_handle().IsValid())
70       wake_up_queue_.erase(queue->heap_handle());
71   }
72 
73   std::optional<WakeUp> new_wake_up = GetNextDelayedWakeUp();
74 
75   if (previous_queue_resolution &&
76       *previous_queue_resolution == WakeUpResolution::kHigh) {
77     pending_high_res_wake_up_count_--;
78   }
79   if (wake_up && wake_up->resolution == WakeUpResolution::kHigh)
80     pending_high_res_wake_up_count_++;
81   DCHECK_GE(pending_high_res_wake_up_count_, 0);
82 
83   if (new_wake_up != previous_wake_up)
84     OnNextWakeUpChanged(lazy_now, GetNextDelayedWakeUp());
85 }
86 
MoveReadyDelayedTasksToWorkQueues(LazyNow * lazy_now,EnqueueOrder enqueue_order)87 void WakeUpQueue::MoveReadyDelayedTasksToWorkQueues(
88     LazyNow* lazy_now,
89     EnqueueOrder enqueue_order) {
90   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
91   bool update_needed = false;
92   while (!wake_up_queue_.empty() &&
93          wake_up_queue_.top().wake_up.earliest_time() <= lazy_now->Now()) {
94     internal::TaskQueueImpl* queue = wake_up_queue_.top().queue;
95     // OnWakeUp() is expected to update the next wake-up for this queue with
96     // SetNextWakeUpForQueue(), thus allowing us to make progress.
97     queue->OnWakeUp(lazy_now, enqueue_order);
98     update_needed = true;
99   }
100 
101   if (!update_needed || wake_up_queue_.empty())
102     return;
103   // If any queue was notified, possibly update following queues. This ensures
104   // the wake up is up to date, which is necessary because calling OnWakeUp() on
105   // a throttled queue may affect state that is shared between other related
106   // throttled queues. The wake up for an affected queue might be pushed back
107   // and needs to be updated. This is done lazily only once the related queue
108   // becomes the next one to wake up, since that wake up can't be moved up.
109   // `wake_up_queue_` is non-empty here, per the condition above.
110   internal::TaskQueueImpl* queue = wake_up_queue_.top().queue;
111   queue->UpdateWakeUp(lazy_now);
112   while (!wake_up_queue_.empty()) {
113     internal::TaskQueueImpl* old_queue =
114         std::exchange(queue, wake_up_queue_.top().queue);
115     if (old_queue == queue)
116       break;
117     queue->UpdateWakeUp(lazy_now);
118   }
119 }
120 
GetNextDelayedWakeUp() const121 std::optional<WakeUp> WakeUpQueue::GetNextDelayedWakeUp() const {
122   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
123   if (wake_up_queue_.empty())
124     return std::nullopt;
125   WakeUp wake_up = wake_up_queue_.top().wake_up;
126   // `wake_up.resolution` is not meaningful since it may be different from
127   // has_pending_high_resolution_tasks(). Return WakeUpResolution::kLow here to
128   // simplify comparison between wake ups.
129   // TODO(1153139): Drive resolution by DelayPolicy and return
130   // has_pending_high_resolution_tasks() here.
131   wake_up.resolution = WakeUpResolution::kLow;
132   return wake_up;
133 }
134 
AsValue(TimeTicks now) const135 Value::Dict WakeUpQueue::AsValue(TimeTicks now) const {
136   Value::Dict state;
137   state.Set("name", GetName());
138   // TODO(crbug.com/1334256): Make base::Value able to store an int64_t and
139   // remove this cast.
140   state.Set("registered_delay_count", checked_cast<int>(wake_up_queue_.size()));
141   if (!wake_up_queue_.empty()) {
142     TimeDelta delay = wake_up_queue_.top().wake_up.time - now;
143     state.Set("next_delay_ms", delay.InMillisecondsF());
144   }
145   return state;
146 }
147 
DefaultWakeUpQueue(scoped_refptr<internal::AssociatedThreadId> associated_thread,internal::SequenceManagerImpl * sequence_manager)148 DefaultWakeUpQueue::DefaultWakeUpQueue(
149     scoped_refptr<internal::AssociatedThreadId> associated_thread,
150     internal::SequenceManagerImpl* sequence_manager)
151     : WakeUpQueue(std::move(associated_thread)),
152       sequence_manager_(sequence_manager) {}
153 
154 DefaultWakeUpQueue::~DefaultWakeUpQueue() = default;
155 
OnNextWakeUpChanged(LazyNow * lazy_now,std::optional<WakeUp> wake_up)156 void DefaultWakeUpQueue::OnNextWakeUpChanged(LazyNow* lazy_now,
157                                              std::optional<WakeUp> wake_up) {
158   sequence_manager_->SetNextWakeUp(lazy_now, wake_up);
159 }
160 
UnregisterQueue(internal::TaskQueueImpl * queue)161 void DefaultWakeUpQueue::UnregisterQueue(internal::TaskQueueImpl* queue) {
162   DCHECK_EQ(queue->wake_up_queue(), this);
163   LazyNow lazy_now(sequence_manager_->main_thread_clock());
164   SetNextWakeUpForQueue(queue, &lazy_now, std::nullopt);
165 }
166 
GetName() const167 const char* DefaultWakeUpQueue::GetName() const {
168   return "DefaultWakeUpQueue";
169 }
170 
NonWakingWakeUpQueue(scoped_refptr<internal::AssociatedThreadId> associated_thread)171 NonWakingWakeUpQueue::NonWakingWakeUpQueue(
172     scoped_refptr<internal::AssociatedThreadId> associated_thread)
173     : WakeUpQueue(std::move(associated_thread)) {}
174 
175 NonWakingWakeUpQueue::~NonWakingWakeUpQueue() = default;
176 
OnNextWakeUpChanged(LazyNow * lazy_now,std::optional<WakeUp> wake_up)177 void NonWakingWakeUpQueue::OnNextWakeUpChanged(LazyNow* lazy_now,
178                                                std::optional<WakeUp> wake_up) {}
179 
GetName() const180 const char* NonWakingWakeUpQueue::GetName() const {
181   return "NonWakingWakeUpQueue";
182 }
183 
UnregisterQueue(internal::TaskQueueImpl * queue)184 void NonWakingWakeUpQueue::UnregisterQueue(internal::TaskQueueImpl* queue) {
185   DCHECK_EQ(queue->wake_up_queue(), this);
186   SetNextWakeUpForQueue(queue, nullptr, std::nullopt);
187 }
188 
189 }  // namespace internal
190 }  // namespace sequence_manager
191 }  // namespace base
192