blob: 2e08584ac334c945cf86da64fa3153f8de7afd78 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/disk_cache/sql/eviction_candidate_aggregator.h"
#include <algorithm>
#include <iterator>
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
namespace disk_cache {
EvictionCandidateAggregator::EvictionCandidate::EvictionCandidate(
SqlPersistentStore::ResId res_id,
SqlPersistentStore::ShardId shard_id,
int64_t bytes_usage,
base::Time last_used)
: res_id(res_id),
shard_id(shard_id),
bytes_usage(bytes_usage),
last_used(last_used) {}
EvictionCandidateAggregator::EvictionCandidate::~EvictionCandidate() = default;
EvictionCandidateAggregator::EvictionCandidate::EvictionCandidate(
EvictionCandidate&&) = default;
EvictionCandidateAggregator::EvictionCandidate&
EvictionCandidateAggregator::EvictionCandidate::operator=(EvictionCandidate&&) =
default;
EvictionCandidateAggregator::EvictionCandidateAggregator(
int64_t size_to_be_removed,
std::vector<scoped_refptr<base::SequencedTaskRunner>> task_runners)
: size_to_be_removed_(size_to_be_removed),
task_runners_(std::move(task_runners)),
selected_callbacks_(task_runners_.size()) {
candidates_per_shard_.reserve(task_runners_.size());
}
void EvictionCandidateAggregator::OnCandidate(
SqlPersistentStore::ShardId shard_id,
EvictionCandidateList candidate,
EvictionCandidateSelectedCallback selected_callback) {
CHECK(task_runners_[shard_id.value()]->RunsTasksInCurrentSequence());
std::vector<EvictionCandidateList> candidates_per_shard;
std::vector<EvictionCandidateSelectedCallback> selected_callbacks;
if (!AddCandidates(shard_id, std::move(candidate),
std::move(selected_callback), candidates_per_shard,
selected_callbacks)) {
return;
}
AggregateCandidatesAndRunCallbacks(std::move(candidates_per_shard),
std::move(selected_callbacks), shard_id);
}
EvictionCandidateAggregator::~EvictionCandidateAggregator() = default;
bool EvictionCandidateAggregator::AddCandidates(
SqlPersistentStore::ShardId shard_id,
EvictionCandidateList new_candidates,
EvictionCandidateSelectedCallback new_selected_callback,
std::vector<EvictionCandidateList>& candidates_per_shard_out,
std::vector<EvictionCandidateSelectedCallback>& selected_callbacks_out) {
base::AutoLock auto_lock(lock_);
candidates_per_shard_.emplace_back(std::move(new_candidates));
selected_callbacks_[shard_id.value()] = std::move(new_selected_callback);
if (candidates_per_shard_.size() != GetSizeOfShards()) {
return false;
}
candidates_per_shard_out = std::move(candidates_per_shard_);
selected_callbacks_out = std::move(selected_callbacks_);
return true;
}
void EvictionCandidateAggregator::AggregateCandidatesAndRunCallbacks(
std::vector<EvictionCandidateList> candidates_per_shard,
std::vector<EvictionCandidateSelectedCallback> selected_callbacks,
SqlPersistentStore::ShardId last_shard_id) {
size_t all_candidate_count = 0;
for (const auto& candidates : candidates_per_shard) {
all_candidate_count += candidates.size();
}
EvictionCandidateList all_candidates;
all_candidates.reserve(all_candidate_count);
for (auto& candidates : candidates_per_shard) {
all_candidates.insert(all_candidates.end(),
std::make_move_iterator(candidates.begin()),
std::make_move_iterator(candidates.end()));
}
// Sort candidates by last_used time, oldest first.
std::sort(
all_candidates.begin(), all_candidates.end(),
[](const auto& a, const auto& b) { return a.last_used < b.last_used; });
std::vector<std::vector<SqlPersistentStore::ResId>>
selected_res_ids_per_shard(GetSizeOfShards());
std::vector<int64_t> bytes_usage_per_shard(GetSizeOfShards());
int64_t removed_total_size = 0;
for (const EvictionCandidate& candidate : all_candidates) {
removed_total_size += candidate.bytes_usage;
bytes_usage_per_shard[candidate.shard_id.value()] += candidate.bytes_usage;
selected_res_ids_per_shard[candidate.shard_id.value()].emplace_back(
candidate.res_id);
if (removed_total_size > size_to_be_removed_) {
break;
}
}
// Post the eviction tasks back to each shard.
for (size_t i = 0; i < GetSizeOfShards(); ++i) {
if (i == last_shard_id.value()) {
// The last shard's callback is run on the current thread, so we skip
// it here.
continue;
}
task_runners_[i]->PostTask(
FROM_HERE,
base::BindOnce(std::move(selected_callbacks[i]),
std::move(selected_res_ids_per_shard[i]),
bytes_usage_per_shard[i], base::TimeTicks::Now()));
}
// Run the last shard's callback on the current thread to avoid an
// unnecessary thread hop.
DCHECK(task_runners_[last_shard_id.value()]->RunsTasksInCurrentSequence());
std::move(selected_callbacks[last_shard_id.value()])
.Run(std::move(selected_res_ids_per_shard[last_shard_id.value()]),
bytes_usage_per_shard[last_shard_id.value()],
base::TimeTicks::Now());
}
size_t EvictionCandidateAggregator::GetSizeOfShards() const {
return task_runners_.size();
}
} // namespace disk_cache