xref: /aosp_15_r20/external/cronet/net/dns/system_dns_config_change_notifier.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 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/system_dns_config_change_notifier.h"
6 
7 #include <map>
8 #include <optional>
9 #include <utility>
10 
11 #include "base/check_op.h"
12 #include "base/functional/bind.h"
13 #include "base/location.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/sequence_checker.h"
17 #include "base/synchronization/lock.h"
18 #include "base/task/sequenced_task_runner.h"
19 #include "base/task/task_traits.h"
20 #include "base/task/thread_pool.h"
21 #include "net/dns/dns_config_service.h"
22 
23 namespace net {
24 
25 namespace {
26 
27 // Internal information and handling for a registered Observer. Handles
28 // posting to and DCHECKing the correct sequence for the Observer.
29 class WrappedObserver {
30  public:
WrappedObserver(SystemDnsConfigChangeNotifier::Observer * observer)31   explicit WrappedObserver(SystemDnsConfigChangeNotifier::Observer* observer)
32       : task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
33         observer_(observer) {}
34 
35   WrappedObserver(const WrappedObserver&) = delete;
36   WrappedObserver& operator=(const WrappedObserver&) = delete;
37 
~WrappedObserver()38   ~WrappedObserver() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
39 
OnNotifyThreadsafe(std::optional<DnsConfig> config)40   void OnNotifyThreadsafe(std::optional<DnsConfig> config) {
41     task_runner_->PostTask(
42         FROM_HERE,
43         base::BindOnce(&WrappedObserver::OnNotify,
44                        weak_ptr_factory_.GetWeakPtr(), std::move(config)));
45   }
46 
OnNotify(std::optional<DnsConfig> config)47   void OnNotify(std::optional<DnsConfig> config) {
48     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
49     DCHECK(!config || config.value().IsValid());
50 
51     observer_->OnSystemDnsConfigChanged(std::move(config));
52   }
53 
54  private:
55   scoped_refptr<base::SequencedTaskRunner> task_runner_;
56   const raw_ptr<SystemDnsConfigChangeNotifier::Observer> observer_;
57 
58   SEQUENCE_CHECKER(sequence_checker_);
59   base::WeakPtrFactory<WrappedObserver> weak_ptr_factory_{this};
60 };
61 
62 }  // namespace
63 
64 // Internal core to be destroyed via base::OnTaskRunnerDeleter to ensure
65 // sequence safety.
66 class SystemDnsConfigChangeNotifier::Core {
67  public:
Core(scoped_refptr<base::SequencedTaskRunner> task_runner,std::unique_ptr<DnsConfigService> dns_config_service)68   Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
69        std::unique_ptr<DnsConfigService> dns_config_service)
70       : task_runner_(std::move(task_runner)) {
71     DCHECK(task_runner_);
72     DCHECK(dns_config_service);
73 
74     DETACH_FROM_SEQUENCE(sequence_checker_);
75 
76     task_runner_->PostTask(FROM_HERE,
77                            base::BindOnce(&Core::SetAndStartDnsConfigService,
78                                           weak_ptr_factory_.GetWeakPtr(),
79                                           std::move(dns_config_service)));
80   }
81 
82   Core(const Core&) = delete;
83   Core& operator=(const Core&) = delete;
84 
~Core()85   ~Core() {
86     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87     DCHECK(wrapped_observers_.empty());
88   }
89 
AddObserver(Observer * observer)90   void AddObserver(Observer* observer) {
91     // Create wrapped observer outside locking in case construction requires
92     // complex side effects.
93     auto wrapped_observer = std::make_unique<WrappedObserver>(observer);
94 
95     {
96       base::AutoLock lock(lock_);
97 
98       if (config_) {
99         // Even though this is the same sequence as the observer, use the
100         // threadsafe OnNotify to post the notification for both lock and
101         // reentrancy safety.
102         wrapped_observer->OnNotifyThreadsafe(config_);
103       }
104 
105       DCHECK_EQ(0u, wrapped_observers_.count(observer));
106       wrapped_observers_.emplace(observer, std::move(wrapped_observer));
107     }
108   }
109 
RemoveObserver(Observer * observer)110   void RemoveObserver(Observer* observer) {
111     // Destroy wrapped observer outside locking in case destruction requires
112     // complex side effects.
113     std::unique_ptr<WrappedObserver> removed_wrapped_observer;
114 
115     {
116       base::AutoLock lock(lock_);
117       auto it = wrapped_observers_.find(observer);
118       DCHECK(it != wrapped_observers_.end());
119       removed_wrapped_observer = std::move(it->second);
120       wrapped_observers_.erase(it);
121     }
122   }
123 
RefreshConfig()124   void RefreshConfig() {
125     task_runner_->PostTask(FROM_HERE,
126                            base::BindOnce(&Core::TriggerRefreshConfig,
127                                           weak_ptr_factory_.GetWeakPtr()));
128   }
129 
SetDnsConfigServiceForTesting(std::unique_ptr<DnsConfigService> dns_config_service,base::OnceClosure done_cb)130   void SetDnsConfigServiceForTesting(
131       std::unique_ptr<DnsConfigService> dns_config_service,
132       base::OnceClosure done_cb) {
133     DCHECK(dns_config_service);
134     task_runner_->PostTask(FROM_HERE,
135                            base::BindOnce(&Core::SetAndStartDnsConfigService,
136                                           weak_ptr_factory_.GetWeakPtr(),
137                                           std::move(dns_config_service)));
138     if (done_cb) {
139       task_runner_->PostTaskAndReply(
140           FROM_HERE,
141           base::BindOnce(&Core::TriggerRefreshConfig,
142                          weak_ptr_factory_.GetWeakPtr()),
143           std::move(done_cb));
144     }
145   }
146 
147  private:
SetAndStartDnsConfigService(std::unique_ptr<DnsConfigService> dns_config_service)148   void SetAndStartDnsConfigService(
149       std::unique_ptr<DnsConfigService> dns_config_service) {
150     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
151 
152     dns_config_service_ = std::move(dns_config_service);
153     dns_config_service_->WatchConfig(base::BindRepeating(
154         &Core::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
155   }
156 
OnConfigChanged(const DnsConfig & config)157   void OnConfigChanged(const DnsConfig& config) {
158     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
159     base::AutoLock lock(lock_);
160 
161     // |config_| is |std::nullopt| if most recent config was invalid (or no
162     // valid config has yet been read), so convert |config| to a similar form
163     // before comparing for change.
164     std::optional<DnsConfig> new_config;
165     if (config.IsValid())
166       new_config = config;
167 
168     if (config_ == new_config)
169       return;
170 
171     config_ = std::move(new_config);
172 
173     for (auto& wrapped_observer : wrapped_observers_) {
174       wrapped_observer.second->OnNotifyThreadsafe(config_);
175     }
176   }
177 
TriggerRefreshConfig()178   void TriggerRefreshConfig() {
179     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
180     dns_config_service_->RefreshConfig();
181   }
182 
183   // Fields that may be accessed from any sequence. Must protect access using
184   // |lock_|.
185   mutable base::Lock lock_;
186   // Only stores valid configs. |std::nullopt| if most recent config was
187   // invalid (or no valid config has yet been read).
188   std::optional<DnsConfig> config_;
189   std::map<Observer*, std::unique_ptr<WrappedObserver>> wrapped_observers_;
190 
191   // Fields valid only on |task_runner_|.
192   scoped_refptr<base::SequencedTaskRunner> task_runner_;
193   SEQUENCE_CHECKER(sequence_checker_);
194   std::unique_ptr<DnsConfigService> dns_config_service_;
195   base::WeakPtrFactory<Core> weak_ptr_factory_{this};
196 };
197 
SystemDnsConfigChangeNotifier()198 SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier()
199     : SystemDnsConfigChangeNotifier(
200           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
201           DnsConfigService::CreateSystemService()) {}
202 
SystemDnsConfigChangeNotifier(scoped_refptr<base::SequencedTaskRunner> task_runner,std::unique_ptr<DnsConfigService> dns_config_service)203 SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier(
204     scoped_refptr<base::SequencedTaskRunner> task_runner,
205     std::unique_ptr<DnsConfigService> dns_config_service)
206     : core_(nullptr, base::OnTaskRunnerDeleter(task_runner)) {
207   if (dns_config_service)
208     core_.reset(new Core(task_runner, std::move(dns_config_service)));
209 }
210 
211 SystemDnsConfigChangeNotifier::~SystemDnsConfigChangeNotifier() = default;
212 
AddObserver(Observer * observer)213 void SystemDnsConfigChangeNotifier::AddObserver(Observer* observer) {
214   if (core_)
215     core_->AddObserver(observer);
216 }
217 
RemoveObserver(Observer * observer)218 void SystemDnsConfigChangeNotifier::RemoveObserver(Observer* observer) {
219   if (core_)
220     core_->RemoveObserver(observer);
221 }
222 
RefreshConfig()223 void SystemDnsConfigChangeNotifier::RefreshConfig() {
224   if (core_)
225     core_->RefreshConfig();
226 }
227 
SetDnsConfigServiceForTesting(std::unique_ptr<DnsConfigService> dns_config_service,base::OnceClosure done_cb)228 void SystemDnsConfigChangeNotifier::SetDnsConfigServiceForTesting(
229     std::unique_ptr<DnsConfigService> dns_config_service,
230     base::OnceClosure done_cb) {
231   DCHECK(core_);
232   DCHECK(dns_config_service);
233 
234   core_->SetDnsConfigServiceForTesting(  // IN-TEST
235       std::move(dns_config_service), std::move(done_cb));
236 }
237 
238 }  // namespace net
239