910e62b5创建于 1月15日历史提交
// 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