1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_PREFETCH_CACHE_H_
#define SERVICES_NETWORK_PREFETCH_CACHE_H_
#include <map>
#include <set>
#include <utility>
#include "base/component_export.h"
#include "base/containers/linked_list.h"
#include "base/containers/queue.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/network_isolation_key.h"
#include "url/gurl.h"
namespace net {
class NetworkIsolationKey;
} // namespace net
namespace network {
class PrefetchURLLoaderClient;
struct ResourceRequest;
// PrefetchCache implements caching of PrefetchURLLoaderClient objects for
// NetworkContexts. There is at most one created per NetworkContext.
class COMPONENT_EXPORT(NETWORK_SERVICE) PrefetchCache final {
public:
// The maximum age a prefetch is permitted to live for without being claimed
// by a render process.
// TODO(crbug.com/342445996): Allow this to be set dynamically by a feature
// param.
static constexpr auto kMaxAge = base::Minutes(5);
// If several prefetches are created close together, it is wasteful to wake up
// once for each one, so permit prefetches that have reached
// `kMaxAge` - `kExpirySlack` to also be expired.
// TODO(crbug.com/342445996): Allow this to be set dynamically by a feature
// param.
static constexpr auto kExpirySlack = base::Seconds(1);
PrefetchCache();
PrefetchCache(const PrefetchCache&) = delete;
PrefetchCache& operator=(const PrefetchCache&) = delete;
~PrefetchCache();
// Instantiates and inserts a new PrefetchURLLoaderClient. If a matching
// request (same NIK and URL) already exists in the cache, returns nullptr.
// The returned PrefetchURLLoaderClient is owned by this object. The returned
// pointer is safe to use synchronously as long as nothing calls Erase() on
// it, but it should not be used again after returning to the run loop as it
// might be deleted asynchronously.
PrefetchURLLoaderClient* Emplace(const ResourceRequest& request);
// Finds a PrefetchURLLoaderClient matching `nik` and `url`. Returns nullptr
// if nothing is found. As with Emplace(), the returned pointer should only be
// used synchronously.
PrefetchURLLoaderClient* Lookup(const net::NetworkIsolationKey& nik,
const GURL& url);
// Prevents `client` from being returned by future calls to Lookup() and
// permits a new request with the same key to be created by Emplace().
// `client` must exist in the cache.
void Consume(PrefetchURLLoaderClient* client);
// Removes `client` from the cache and deletes it. `client` must have been
// created by Emplace() and not already erased.
void Erase(PrefetchURLLoaderClient* client);
// Erases `client` after `kEraseGraceTime` has expired. The purpose is to
// permit a new transaction from a renderer to reach the HttpCache code before
// this client is erased, so that it can take over the cache lock if possible,
// avoiding the entry being truncated.
//
// This is a temporary feature to maximise the chances of reusing the disk
// cache entry when the feature kNetworkContextPrefetchUseMatches is not
// enabled.
//
// TODO(crbug.com/342445996): Remove this method and associated code after
// kNetworkContextPrefetchUseMatches has been permanently enabled.
void DelayedErase(PrefetchURLLoaderClient* client);
private:
// This is not a std::list because we want to be able to remove an item from
// the cache by pointer.
using ListType = base::LinkedList<PrefetchURLLoaderClient>;
// The references in the KeyType point inside the PrefetchURLLoaderClient that
// is the value of the map, so they are guaranteed to be valid.
using KeyType = std::pair<const net::NetworkIsolationKey&, const GURL&>;
// The value_type of the map is const to reduce the risk of accidentally
// changing it, since the key's references point into the value.
using MapType = std::map<KeyType, const raw_ptr<PrefetchURLLoaderClient>>;
using ClientStorage = std::set<std::unique_ptr<PrefetchURLLoaderClient>,
base::UniquePtrComparator>;
struct PendingErasure {
// The client is referred to by its key rather than a pointer, so that there
// is no dangling reference if something else erases the client first.
net::NetworkIsolationKey nik;
GURL url;
base::TimeTicks erase_time;
};
using EraseQueue = base::queue<PendingErasure>;
// Deletes any expired cache entries and then restarts the timer if needed.
void OnTimer();
// Deletes anything in `delayed_erase_queue_` that is due for deletion, and
// then schedules another call if the queue is still non-empty.
void DoDelayedErases();
// Removes and deletes the oldest entry from the cache.
void EraseOldest();
// Removes an entry from the cache without deleting it. `client` must be in
// the cache.
void RemoveFromCache(PrefetchURLLoaderClient* client);
// Removes `client` from `client_storage_`, deleting it. `client` must exist.
void EraseFromStorage(PrefetchURLLoaderClient* client);
// Starts the timer to fire when the next cache entry will expire. `now`
// should be the current time. It is optional because some callers have it
// handy and some don't.
void StartTimer(base::TimeTicks now = base::TimeTicks::Now());
// Schedules a task to call DoDelayedErases() the next time something in
// `delayed_erase_queue_` needs to be erased. `now` should be the return value
// from a recent call to `base::TimeTicks::Now()`.
void SchedulePendingErases(base::TimeTicks now);
// Performing a find() on `map_` is sufficiently messy that it's worth
// encapsulating in a separate method.
MapType::iterator FindInMap(const net::NetworkIsolationKey& nik,
const GURL& url);
// Storage for all the PrefetchURLLoaderClients created by this object,
// regardless if Consume() has been called for them or not. `list_` and `map_`
// contain references into these objects, so must be destroyed first.
ClientStorage client_storage_;
// The PrefetchURLLoaderClients are stored in a list to permit deletion and
// finding the oldest in O(1) time. `list_.head()` is the oldest item and
// `list_.tail()` is the newest. `list_` does not own the objects. They are
// owned by `client_storage_`.
ListType list_;
// They are referenced from a map, permitting O(log N) lookup.
MapType map_;
// Timer. If `list_` is non-empty, it is set to go off when the oldest item in
// `list_` will expire.
base::OneShotTimer expiry_timer_;
// Queue for DelayedErase(). Entries will be deleted when their `erase_time`
// is reached.
EraseQueue delayed_erase_queue_;
// Initialized from kNetworkContextPrefetchMaxLoaders feature flag.
const size_t max_size_;
// Initialized from "erase_grace_time" parameter to "NetworkContextPrefetch"
// feature.
const base::TimeDelta erase_grace_time_;
base::WeakPtrFactory<PrefetchCache> weak_factory_{this};
};
} // namespace network
#endif // SERVICES_NETWORK_PREFETCH_CACHE_H_
|