123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/dns/serial_worker.h"
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/check_op.h"
- #include "base/location.h"
- #include "base/notreached.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/timer/timer.h"
- #include "net/base/backoff_entry.h"
- namespace net {
- namespace {
- // Default retry configuration. Only in effect if |max_number_of_retries| is
- // greater than 0.
- constexpr BackoffEntry::Policy kDefaultBackoffPolicy = {
- 0, // Number of initial errors to ignore without backoff.
- 5000, // Initial delay for backoff in ms: 5 seconds.
- 2, // Factor to multiply for exponential backoff.
- 0, // Fuzzing percentage.
- -1, // No maximum delay.
- -1, // Don't discard entry.
- false // Don't use initial delay unless the last was an error.
- };
- } // namespace
- namespace {
- std::unique_ptr<SerialWorker::WorkItem> DoWork(
- std::unique_ptr<SerialWorker::WorkItem> work_item) {
- DCHECK(work_item);
- work_item->DoWork();
- return work_item;
- }
- } // namespace
- void SerialWorker::WorkItem::FollowupWork(base::OnceClosure closure) {
- std::move(closure).Run();
- }
- SerialWorker::SerialWorker(int max_number_of_retries,
- const net::BackoffEntry::Policy* backoff_policy)
- : max_number_of_retries_(max_number_of_retries),
- backoff_entry_(backoff_policy ? backoff_policy : &kDefaultBackoffPolicy) {
- }
- SerialWorker::~SerialWorker() = default;
- void SerialWorker::WorkNow() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Not a retry; reset failure count and cancel the pending retry (if any).
- backoff_entry_.Reset();
- retry_timer_.Stop();
- WorkNowInternal();
- }
- void SerialWorker::WorkNowInternal() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- switch (state_) {
- case State::kIdle:
- // We are posting weak pointer to OnWorkJobFinished to avoid a leak when
- // PostTaskAndReply fails to post task back to the original
- // task runner. In this case the callback is not destroyed, and the
- // weak reference allows SerialWorker instance to be deleted.
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE,
- {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(&DoWork, CreateWorkItem()),
- base::BindOnce(&SerialWorker::OnDoWorkFinished, AsWeakPtr()));
- state_ = State::kWorking;
- return;
- case State::kWorking:
- // Remember to re-read after `DoWork()` finishes.
- state_ = State::kPending;
- return;
- case State::kCancelled:
- case State::kPending:
- return;
- }
- }
- void SerialWorker::Cancel() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- state_ = State::kCancelled;
- }
- void SerialWorker::OnDoWorkFinished(std::unique_ptr<WorkItem> work_item) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- switch (state_) {
- case State::kCancelled:
- return;
- case State::kWorking: {
- WorkItem* work_item_ptr = work_item.get();
- work_item_ptr->FollowupWork(
- base::BindOnce(&SerialWorker::OnFollowupWorkFinished,
- weak_factory_.GetWeakPtr(), std::move(work_item)));
- return;
- }
- case State::kPending: {
- RerunWork(std::move(work_item));
- return;
- }
- default:
- NOTREACHED() << "Unexpected state " << static_cast<int>(state_);
- }
- }
- void SerialWorker::OnFollowupWorkFinished(std::unique_ptr<WorkItem> work_item) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- switch (state_) {
- case State::kCancelled:
- return;
- case State::kWorking:
- state_ = State::kIdle;
- if (OnWorkFinished(std::move(work_item)) ||
- backoff_entry_.failure_count() >= max_number_of_retries_) {
- backoff_entry_.Reset();
- } else {
- backoff_entry_.InformOfRequest(/*succeeded=*/false);
- // Try again after a delay.
- retry_timer_.Start(FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
- this, &SerialWorker::WorkNowInternal);
- }
- return;
- case State::kPending:
- RerunWork(std::move(work_item));
- return;
- default:
- NOTREACHED() << "Unexpected state " << static_cast<int>(state_);
- }
- }
- void SerialWorker::RerunWork(std::unique_ptr<WorkItem> work_item) {
- // `WorkNow()` was retriggered while working, so need to redo work
- // immediately to ensure up-to-date results. Reuse `work_item` rather than
- // returning it to the derived class (and letting it potentially act on a
- // potential obsolete result).
- DCHECK_EQ(state_, State::kPending);
- state_ = State::kWorking;
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE,
- {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(&DoWork, std::move(work_item)),
- base::BindOnce(&SerialWorker::OnDoWorkFinished, AsWeakPtr()));
- }
- const BackoffEntry& SerialWorker::GetBackoffEntryForTesting() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return backoff_entry_;
- }
- const base::OneShotTimer& SerialWorker::GetRetryTimerForTesting() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return retry_timer_;
- }
- base::WeakPtr<SerialWorker> SerialWorker::AsWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- } // namespace net
|