xref: /aosp_15_r20/external/cronet/net/dns/serial_worker.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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