1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/serial_worker.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/location.h"
14 #include "base/notreached.h"
15 #include "base/task/thread_pool.h"
16 #include "base/timer/timer.h"
17 #include "net/base/backoff_entry.h"
18
19 namespace net {
20
21 namespace {
22 // Default retry configuration. Only in effect if |max_number_of_retries| is
23 // greater than 0.
24 constexpr BackoffEntry::Policy kDefaultBackoffPolicy = {
25 0, // Number of initial errors to ignore without backoff.
26 5000, // Initial delay for backoff in ms: 5 seconds.
27 2, // Factor to multiply for exponential backoff.
28 0, // Fuzzing percentage.
29 -1, // No maximum delay.
30 -1, // Don't discard entry.
31 false // Don't use initial delay unless the last was an error.
32 };
33 } // namespace
34
35 namespace {
DoWork(std::unique_ptr<SerialWorker::WorkItem> work_item)36 std::unique_ptr<SerialWorker::WorkItem> DoWork(
37 std::unique_ptr<SerialWorker::WorkItem> work_item) {
38 DCHECK(work_item);
39 work_item->DoWork();
40 return work_item;
41 }
42 } // namespace
43
FollowupWork(base::OnceClosure closure)44 void SerialWorker::WorkItem::FollowupWork(base::OnceClosure closure) {
45 std::move(closure).Run();
46 }
47
SerialWorker(int max_number_of_retries,const net::BackoffEntry::Policy * backoff_policy)48 SerialWorker::SerialWorker(int max_number_of_retries,
49 const net::BackoffEntry::Policy* backoff_policy)
50 : max_number_of_retries_(max_number_of_retries),
51 backoff_entry_(backoff_policy ? backoff_policy : &kDefaultBackoffPolicy) {
52 }
53
54 SerialWorker::~SerialWorker() = default;
55
WorkNow()56 void SerialWorker::WorkNow() {
57 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
58 // Not a retry; reset failure count and cancel the pending retry (if any).
59 backoff_entry_.Reset();
60 retry_timer_.Stop();
61 WorkNowInternal();
62 }
63
WorkNowInternal()64 void SerialWorker::WorkNowInternal() {
65 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
66 switch (state_) {
67 case State::kIdle:
68 // We are posting weak pointer to OnWorkJobFinished to avoid a leak when
69 // PostTaskAndReply fails to post task back to the original
70 // task runner. In this case the callback is not destroyed, and the
71 // weak reference allows SerialWorker instance to be deleted.
72 base::ThreadPool::PostTaskAndReplyWithResult(
73 FROM_HERE,
74 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
75 base::BindOnce(&DoWork, CreateWorkItem()),
76 base::BindOnce(&SerialWorker::OnDoWorkFinished, AsWeakPtr()));
77 state_ = State::kWorking;
78 return;
79 case State::kWorking:
80 // Remember to re-read after `DoWork()` finishes.
81 state_ = State::kPending;
82 return;
83 case State::kCancelled:
84 case State::kPending:
85 return;
86 }
87 }
88
Cancel()89 void SerialWorker::Cancel() {
90 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
91 state_ = State::kCancelled;
92 }
93
OnDoWorkFinished(std::unique_ptr<WorkItem> work_item)94 void SerialWorker::OnDoWorkFinished(std::unique_ptr<WorkItem> work_item) {
95 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
96
97 switch (state_) {
98 case State::kCancelled:
99 return;
100 case State::kWorking: {
101 WorkItem* work_item_ptr = work_item.get();
102 work_item_ptr->FollowupWork(
103 base::BindOnce(&SerialWorker::OnFollowupWorkFinished,
104 weak_factory_.GetWeakPtr(), std::move(work_item)));
105 return;
106 }
107 case State::kPending: {
108 RerunWork(std::move(work_item));
109 return;
110 }
111 default:
112 NOTREACHED() << "Unexpected state " << static_cast<int>(state_);
113 }
114 }
115
OnFollowupWorkFinished(std::unique_ptr<WorkItem> work_item)116 void SerialWorker::OnFollowupWorkFinished(std::unique_ptr<WorkItem> work_item) {
117 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
118 switch (state_) {
119 case State::kCancelled:
120 return;
121 case State::kWorking:
122 state_ = State::kIdle;
123 if (OnWorkFinished(std::move(work_item)) ||
124 backoff_entry_.failure_count() >= max_number_of_retries_) {
125 backoff_entry_.Reset();
126 } else {
127 backoff_entry_.InformOfRequest(/*succeeded=*/false);
128
129 // Try again after a delay.
130 retry_timer_.Start(FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
131 this, &SerialWorker::WorkNowInternal);
132 }
133 return;
134 case State::kPending:
135 RerunWork(std::move(work_item));
136 return;
137 default:
138 NOTREACHED() << "Unexpected state " << static_cast<int>(state_);
139 }
140 }
141
RerunWork(std::unique_ptr<WorkItem> work_item)142 void SerialWorker::RerunWork(std::unique_ptr<WorkItem> work_item) {
143 // `WorkNow()` was retriggered while working, so need to redo work
144 // immediately to ensure up-to-date results. Reuse `work_item` rather than
145 // returning it to the derived class (and letting it potentially act on a
146 // potential obsolete result).
147 DCHECK_EQ(state_, State::kPending);
148 state_ = State::kWorking;
149 base::ThreadPool::PostTaskAndReplyWithResult(
150 FROM_HERE,
151 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
152 base::BindOnce(&DoWork, std::move(work_item)),
153 base::BindOnce(&SerialWorker::OnDoWorkFinished, AsWeakPtr()));
154 }
155
GetBackoffEntryForTesting() const156 const BackoffEntry& SerialWorker::GetBackoffEntryForTesting() const {
157 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
158 return backoff_entry_;
159 }
160
GetRetryTimerForTesting() const161 const base::OneShotTimer& SerialWorker::GetRetryTimerForTesting() const {
162 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
163 return retry_timer_;
164 }
165
GetFailureCount() const166 int SerialWorker::GetFailureCount() const {
167 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
168 return backoff_entry_.failure_count();
169 }
170
AsWeakPtr()171 base::WeakPtr<SerialWorker> SerialWorker::AsWeakPtr() {
172 return weak_factory_.GetWeakPtr();
173 }
174
175 } // namespace net
176