blob: b8d06bf5c1d8ea915eb2ca037cc5382b394daf3e [file] [log] [blame]
kenoss1b8ebc22025-04-04 00:11:351// Copyright 2025 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 CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SCHEDULER_H_
6#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SCHEDULER_H_
7
8#include "base/functional/callback.h"
9#include "base/memory/weak_ptr.h"
10#include "content/common/content_export.h"
11
12namespace content {
13
14class PrefetchContainer;
15class PrefetchService;
16
17// Priority of `PrefetchContainer`
18//
19// - Larger values take priority.
20// - If `PrefetchContainer` has a priority larger than `kBurstThreshold`, it
21// will be executed with additional capacity. For more details, see the
22// comment #algorithm in `PrefetchScheduler::Progress()`.
23// - Note that priority should be explicitly re-calculated if related attributes
24// of `PrefetchContainer` are changed. See
25// `PrefetchScheduler::CalculatePriority()`, the call sites, and
26// `PrefetchScheduler::NotifyAttributeMightChangedAndProgressAsync()`.
27//
28// Maintenance policy:
29//
30// - Put a priority per reason. Think twice before mixing multiple reasons into
31// a priority. (For ease of understanding the behavior.)
32// - It's safe to renumber with preserving the order. Do it if needed.
33//
34// TODO(crbug.com/406403063): Rethink about granularity. We don't stick on the
35// above policy. More rough priorities e.g. kBase/kHigh/kBurst might work well.
Taiyo Mizuhashi53da92d2025-06-23 08:56:1536// This will eventually be done by `PrefetchPriority`(crbug.com/426404355).
kenoss1b8ebc22025-04-04 00:11:3537//
38// See also `PrefetchScheduler::NotifyAttributeMightChangedAndProgressAsync()`
39// when you add a new one.
Taiyo Mizuhashid7c856992025-06-23 08:56:0240enum class PrefetchSchedulerPriority {
kenoss1b8ebc22025-04-04 00:11:3541 // Default.
42 kBase = 0,
43 // For tests. Do not use outside tests.
44 kHighTest = 1,
kenoss1eb925f2025-04-04 05:49:5645 // Priority for prefetch ahead of prerender.
46 kHighAheadOfPrerender = 2,
kenoss1b8ebc22025-04-04 00:11:3547 // It's a threshold for burst. Do not use it in
48 // `PrefetchQueue::CalculatePriority()`. For burst, see a comment of
49 // `PrefetchScheduler`.
50 kBurstThreshold = 10,
51 // For tests. Do not use outside tests.
52 kBurstTest = 11,
Taiyo Mizuhashi53da92d2025-06-23 08:56:1553 // Priority derived from `PrefetchPriority`.
54 kBurstForPrefetchPriority = 12,
kenoss1eb925f2025-04-04 05:49:5655 // Burst priority for prefetch ahead of prerender.
Taiyo Mizuhashi53da92d2025-06-23 08:56:1556 kBurstAheadOfPrerender = 13,
kenoss1b8ebc22025-04-04 00:11:3557};
58
59// Priority queue for prefetches
60//
61// It's a pure data structure and intended to be used in only
62// `PrefetchScheduler`.
63class PrefetchQueue {
64 public:
65 struct Item {
66 Item(base::WeakPtr<PrefetchContainer> prefetch_container,
Taiyo Mizuhashid7c856992025-06-23 08:56:0267 PrefetchSchedulerPriority priority);
kenoss1b8ebc22025-04-04 00:11:3568 ~Item();
69
70 // Movable but not copyable.
71 Item(const Item&&);
72 Item& operator=(const Item&&);
73 Item(const Item&) = delete;
74 Item& operator=(const Item&) = delete;
75
76 base::WeakPtr<PrefetchContainer> prefetch_container;
Taiyo Mizuhashid7c856992025-06-23 08:56:0277 PrefetchSchedulerPriority priority;
kenoss1b8ebc22025-04-04 00:11:3578 };
79
80 PrefetchQueue();
81 ~PrefetchQueue();
82
83 // Not movable nor copyable.
84 PrefetchQueue(const PrefetchQueue&&) = delete;
85 PrefetchQueue& operator=(const PrefetchQueue&&) = delete;
86 PrefetchQueue(const PrefetchQueue&) = delete;
87 PrefetchQueue& operator=(const PrefetchQueue&) = delete;
88
89 size_t size() const { return queue_.size(); }
90
91 void Push(base::WeakPtr<PrefetchContainer> prefetch_container,
Taiyo Mizuhashid7c856992025-06-23 08:56:0292 PrefetchSchedulerPriority priority);
kenoss1b8ebc22025-04-04 00:11:3593
94 // Pops `PrefetchContainer`
95 //
96 // Returns the most high priority and first-in one iff found satisfying:
97 //
98 // - `pred`
99 // - Priority is larger or equal to `threshold_priority`.
100 template <class Predicate>
Taiyo Mizuhashid7c856992025-06-23 08:56:02101 std::optional<PrefetchQueue::Item> Pop(
102 Predicate pred,
103 PrefetchSchedulerPriority threshold_priority) {
kenoss1b8ebc22025-04-04 00:11:35104 for (auto it = queue_.cbegin(); it != queue_.cend(); ++it) {
105 if (it->priority < threshold_priority) {
106 break;
107 }
108
109 if (pred(*it)) {
110 PrefetchQueue::Item ret = std::move(*it);
111 queue_.erase(it);
112 return std::move(ret);
113 }
114 }
115
116 return std::nullopt;
117 }
118
119 // Removes `PrefechContainer`.
120 //
121 // Returns true iff `PrefetchContainer` exists and is removed.
122 bool Remove(base::WeakPtr<PrefetchContainer> prefetch_container);
123 // Updates priority.
124 //
125 // Returns true iff priority is updated.
126 bool MaybeUpdatePriority(PrefetchContainer& prefetch_container,
Taiyo Mizuhashid7c856992025-06-23 08:56:02127 PrefetchSchedulerPriority priority);
kenoss1b8ebc22025-04-04 00:11:35128
129 private:
130 std::vector<PrefetchQueue::Item> queue_;
131};
132
133// Schedules prefetches with limited concurrency.
134//
135// - Limits concurrently running prefetches.
136// - Bursts and use a different limit if a `PrefetchContainer` has priority
137// higher than `PrefetchPriority::kBurstThreshold`.
138// - Manages waiting prefetches with a priority queue.
139// - Prevents stuck (`queue_.size() > 0 && active_set_.size() == 0`).
140//
141// For more details, see
142// https://docs.google.com/document/d/1W0Nk3Nq6NaUXkBppOUC5zyNmhVqMjYShm1bydGYd9qc
143class CONTENT_EXPORT PrefetchScheduler {
144 public:
145 explicit PrefetchScheduler(PrefetchService* prefetch_service);
146 ~PrefetchScheduler();
147
148 // Not movable nor copyable.
149 PrefetchScheduler(const PrefetchScheduler&&) = delete;
150 PrefetchScheduler& operator=(const PrefetchScheduler&&) = delete;
151 PrefetchScheduler(const PrefetchScheduler&) = delete;
152 PrefetchScheduler& operator=(const PrefetchScheduler&) = delete;
153
154 // See `active_set_`.
155 bool IsInActiveSet(const PrefetchContainer& prefetch_container);
156
157 // Modify-and-progress-async methods
158 //
159 // To prevent stuck, this class (and `PrefetchService`) must ensure that
160 // `Progress()` will be called eventually after modification. To ensure that,
161 // these modification methods automatically emit `Progress()` in async
162 // fashion.
163 //
164 // Contract:
165 //
166 // - `PrefetchService` must call `RemoveAndProgressAsync()` before weak
167 // pointer invalidation of the `PrefetchContainer`.
168 // - `PrefetchService` must call
169 // `NotifyAttributeMightChangedAndProgressAsync()` after modification of
170 // `PrefetchContainer` that can change priority.
171 //
172 // [#cant-detect-prefetch-document-manager] Note that `PrefetchScheduler`
173 // can't detect changes of `PrefetchDocumentManager` and
174 // `PrefetchDocumentManager::CanPrefetchNow()`. So, `PrefetchService` must
175 // explicitly call `Progress()`.
kenosse8d8bbb52025-05-13 12:35:04176 //
177 // If `should_progress` is false, doesn't call `ProgressAsync()`.
kenoss0a567362025-05-16 11:18:55178 void PushAndProgress(PrefetchContainer& prefetch_container);
kenoss1b8ebc22025-04-04 00:11:35179 void PushAndProgressAsync(PrefetchContainer& prefetch_container);
180 // Note that this doesn't call `PrefetchService::ResetPrefetchContainer()`.
kenosse8d8bbb52025-05-13 12:35:04181 void RemoveAndProgressAsync(PrefetchContainer& prefetch_container,
182 bool should_progress = true);
kenoss1b8ebc22025-04-04 00:11:35183 void NotifyAttributeMightChangedAndProgressAsync(
kenosse8d8bbb52025-05-13 12:35:04184 PrefetchContainer& prefetch_container,
185 bool should_proggress = true);
kenoss1b8ebc22025-04-04 00:11:35186
187 // Progress scheduling
188 //
189 // This can call methods of `PrefetchService` with
190 // `PassKey<PrefetchScheduler>`, e.g. starting the load and eviction.
191 //
192 // Contract: See the comment [#cant-detect-prefetch-document-manager].
193 void Progress();
194
195 void SetCalculatePriorityForTesting(
Taiyo Mizuhashid7c856992025-06-23 08:56:02196 base::RepeatingCallback<
197 PrefetchSchedulerPriority(const PrefetchContainer&)> callback);
kenoss1b8ebc22025-04-04 00:11:35198
199 private:
Taiyo Mizuhashid7c856992025-06-23 08:56:02200 PrefetchSchedulerPriority CalculatePriority(
kenoss1b8ebc22025-04-04 00:11:35201 const PrefetchContainer& prefetch_container);
202
203 void ProgressAsync();
204 void ProgressOne(base::WeakPtr<PrefetchContainer> prefetch_container);
205
206 // Safety: This class is owned by `PrefetchService` and has the same lifetime.
207 const raw_ptr<PrefetchService> prefetch_service_;
208
209 // Max heap by priority
210 PrefetchQueue queue_;
211 // Active set of prefetches, i.e. prefetches that are in loading state
212 // (roughly).
213 //
214 // We use `std::vector` as set-like containers can't use `base::WeakPtr`.
215 std::vector<base::WeakPtr<PrefetchContainer>> active_set_;
216
217 // Used to reduce unnecessary multiple `Progress()` calls.
218 bool is_progress_scheduled_ = false;
219 // Used to prevent `PrefetchScheduler::ProgressOne()` ->
220 // `PrefetchService::EvictPrefetch()` ->
221 // `PrefetchScheduler::RemoveAndProgressAsync()` trigger
222 // `PrefetchScheduler::ProgressAsync()`.
223 bool in_eviction_ = false;
224
Taiyo Mizuhashid7c856992025-06-23 08:56:02225 base::RepeatingCallback<PrefetchSchedulerPriority(const PrefetchContainer&)>
kenoss1b8ebc22025-04-04 00:11:35226 calculate_priority_for_test_;
227
228#if DCHECK_IS_ON()
229 // Protects against `Progress()` being called recursively.
230 bool progress_reentrancy_guard_ = false;
231#endif
232
233 base::WeakPtrFactory<PrefetchScheduler> weak_method_factory_{this};
234};
235
236} // namespace content
237
238#endif // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_SCHEDULER_H_