171 lines
5.4 KiB
C++
171 lines
5.4 KiB
C++
// 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.
|
|
|
|
#include "net/dns/serial_worker.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/location.h"
|
|
#include "base/notreached.h"
|
|
#include "base/task/thread_pool.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
|