910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_
#define EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_

#include <stddef.h>

#include <algorithm>
#include <memory>
#include <utility>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "extensions/browser/updater/request_queue.h"

namespace extensions {

template <typename T>
RequestQueue<T>::RequestQueue(
    net::BackoffEntry::Policy backoff_policy,
    const base::RepeatingClosure& start_request_callback)
    : backoff_policy_(backoff_policy),
      start_request_callback_(start_request_callback),
      active_request_(std::nullopt) {}

template <typename T>
RequestQueue<T>::~RequestQueue() = default;

template <typename T>
T* RequestQueue<T>::active_request() {
  return active_request_ ? active_request_->fetch.get() : nullptr;
}

template <typename T>
int RequestQueue<T>::active_request_failure_count() {
  DCHECK(active_request_);
  return active_request_->backoff_entry->failure_count();
}

template <typename T>
typename RequestQueue<T>::Request RequestQueue<T>::reset_active_request() {
  DCHECK(active_request_);
  Request request = std::move(*active_request_);
  active_request_.reset();
  return request;
}

template <typename T>
void RequestQueue<T>::ScheduleRequest(std::unique_ptr<T> request) {
  PushImpl(Request(std::unique_ptr<net::BackoffEntry>(
                       new net::BackoffEntry(&backoff_policy_)),
                   std::move(request)));
  StartNextRequest();
}

template <typename T>
void RequestQueue<T>::ScheduleRetriedRequest(
    Request request,
    const base::TimeDelta& min_backoff_delay) {
  DCHECK(request.backoff_entry);
  DCHECK(request.fetch);
  request.backoff_entry->InformOfRequest(false);
  if (request.backoff_entry->GetTimeUntilRelease() < min_backoff_delay) {
    request.backoff_entry->SetCustomReleaseTime(base::TimeTicks::Now() +
                                                min_backoff_delay);
  }
  PushImpl(std::move(request));
}

template <typename T>
void RequestQueue<T>::PushImpl(Request request) {
  pending_requests_.push_back(std::move(request));
  std::push_heap(
      pending_requests_.begin(), pending_requests_.end(), CompareRequests);
}

template <typename T>
bool RequestQueue<T>::empty() const {
  return pending_requests_.empty();
}

template <typename T>
size_t RequestQueue<T>::size() const {
  return pending_requests_.size();
}

template <typename T>
base::TimeTicks RequestQueue<T>::NextReleaseTime() const {
  return pending_requests_.front().backoff_entry->GetReleaseTime();
}

template <typename T>
void RequestQueue<T>::StartNextRequest() {
  if (active_request_) {
    // Already running a request, assume this method will be called again when
    // the request is done.
    return;
  }

  if (empty()) {
    // No requests in the queue, so we're done.
    return;
  }

  base::TimeTicks next_release = NextReleaseTime();
  base::TimeTicks now = base::TimeTicks::Now();
  if (next_release > now) {
    // Not ready for the next update check yet, call this method when it is
    // time.
    timer_.Start(FROM_HERE, next_release - now,
                 base::BindOnce(&RequestQueue<T>::StartNextRequest,
                                base::Unretained(this)));
    return;
  }

  // pop_heap swaps the first and last elements of pending_requests_, and after
  // that assures that the rest of pending_requests_ (excluding the
  // now last/formerly first element) forms a proper heap. After pop_heap
  // [begin, end-1) is a valid heap, and *(end - 1) contains the element that
  // used to be at the top of the heap. Since no elements are actually
  // removed from the container it is safe to read the entry being removed after
  // pop_heap is called (but before pop_back is called).
  std::pop_heap(
      pending_requests_.begin(), pending_requests_.end(), CompareRequests);

  active_request_ = std::move(pending_requests_.back());

  pending_requests_.pop_back();

  start_request_callback_.Run();
}

template <typename T>
void RequestQueue<T>::RetryRequest(const base::TimeDelta& min_backoff_delay) {
  DCHECK(active_request_);
  ScheduleRetriedRequest(reset_active_request(), min_backoff_delay);
}

template <typename T>
typename RequestQueue<T>::iterator RequestQueue<T>::begin() {
  return iterator(pending_requests_.begin());
}

template <typename T>
typename RequestQueue<T>::iterator RequestQueue<T>::end() {
  return iterator(pending_requests_.end());
}

template <typename T>
std::vector<std::unique_ptr<T>> RequestQueue<T>::erase_if(
    base::RepeatingCallback<bool(const T&)> condition) {
  std::vector<std::unique_ptr<T>> erased_fetches;
  for (size_t i = 0; i < pending_requests_.size();) {
    if (condition.Run(*pending_requests_[i].fetch)) {
      erased_fetches.emplace_back(std::move(pending_requests_[i].fetch));
      std::swap(pending_requests_[i],
                pending_requests_[pending_requests_.size() - 1]);
      pending_requests_.pop_back();
    } else {
      i++;
    }
  }
  // We need to maintain a heap structure on pending request in order to extract
  // first ones, but removing might break this structure.
  std::make_heap(pending_requests_.begin(), pending_requests_.end(),
                 CompareRequests);

  return erased_fetches;
}

template <typename T>
void RequestQueue<T>::set_backoff_policy(
    const net::BackoffEntry::Policy backoff_policy) {
  backoff_policy_ = backoff_policy;
}

// static
template <typename T>
bool RequestQueue<T>::CompareRequests(const Request& a, const Request& b) {
  return a.backoff_entry->GetReleaseTime() > b.backoff_entry->GetReleaseTime();
}

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_