blob: 68472cf3da7b2dda5bbbc56643ff078c9b40cea4 [file] [log] [blame]
// Copyright 2019 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/system_dns_config_change_notifier.h"
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/check_op.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "net/dns/dns_config_service.h"
namespace net {
namespace {
// Internal information and handling for a registered Observer. Handles
// posting to and DCHECKing the correct sequence for the Observer.
class WrappedObserver {
public:
explicit WrappedObserver(SystemDnsConfigChangeNotifier::Observer* observer)
: task_runner_(base::SequencedTaskRunnerHandle::Get()),
observer_(observer) {}
~WrappedObserver() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
void OnNotifyThreadsafe(absl::optional<DnsConfig> config) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&WrappedObserver::OnNotify,
weak_ptr_factory_.GetWeakPtr(), std::move(config)));
}
void OnNotify(absl::optional<DnsConfig> config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!config || config.value().IsValid());
observer_->OnSystemDnsConfigChanged(std::move(config));
}
private:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SystemDnsConfigChangeNotifier::Observer* const observer_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<WrappedObserver> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WrappedObserver);
};
} // namespace
// Internal core to be destroyed via base::OnTaskRunnerDeleter to ensure
// sequence safety.
class SystemDnsConfigChangeNotifier::Core {
public:
Core(scoped_refptr<base::SequencedTaskRunner> task_runner,
std::unique_ptr<DnsConfigService> dns_config_service)
: task_runner_(std::move(task_runner)) {
DCHECK(task_runner_);
DCHECK(dns_config_service);
DETACH_FROM_SEQUENCE(sequence_checker_);
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::SetAndStartDnsConfigService,
weak_ptr_factory_.GetWeakPtr(),
std::move(dns_config_service)));
}
~Core() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(wrapped_observers_.empty());
}
void AddObserver(Observer* observer) {
// Create wrapped observer outside locking in case construction requires
// complex side effects.
auto wrapped_observer = std::make_unique<WrappedObserver>(observer);
{
base::AutoLock lock(lock_);
if (config_) {
// Even though this is the same sequence as the observer, use the
// threadsafe OnNotify to post the notification for both lock and
// reentrancy safety.
wrapped_observer->OnNotifyThreadsafe(config_);
}
DCHECK_EQ(0u, wrapped_observers_.count(observer));
wrapped_observers_.emplace(observer, std::move(wrapped_observer));
}
}
void RemoveObserver(Observer* observer) {
// Destroy wrapped observer outside locking in case destruction requires
// complex side effects.
std::unique_ptr<WrappedObserver> removed_wrapped_observer;
{
base::AutoLock lock(lock_);
auto it = wrapped_observers_.find(observer);
DCHECK(it != wrapped_observers_.end());
removed_wrapped_observer = std::move(it->second);
wrapped_observers_.erase(it);
}
}
void RefreshConfig() {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::TriggerRefreshConfig,
weak_ptr_factory_.GetWeakPtr()));
}
void SetDnsConfigServiceForTesting(
std::unique_ptr<DnsConfigService> dns_config_service) {
DCHECK(dns_config_service);
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::SetAndStartDnsConfigService,
weak_ptr_factory_.GetWeakPtr(),
std::move(dns_config_service)));
}
private:
void SetAndStartDnsConfigService(
std::unique_ptr<DnsConfigService> dns_config_service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dns_config_service_ = std::move(dns_config_service);
dns_config_service_->WatchConfig(base::BindRepeating(
&Core::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
}
void OnConfigChanged(const DnsConfig& config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock lock(lock_);
// |config_| is |absl::nullopt| if most recent config was invalid (or no
// valid config has yet been read), so convert |config| to a similar form
// before comparing for change.
absl::optional<DnsConfig> new_config;
if (config.IsValid())
new_config = config;
if (config_ == new_config)
return;
config_ = std::move(new_config);
for (auto& wrapped_observer : wrapped_observers_) {
wrapped_observer.second->OnNotifyThreadsafe(config_);
}
}
void TriggerRefreshConfig() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dns_config_service_->RefreshConfig();
}
// Fields that may be accessed from any sequence. Must protect access using
// |lock_|.
mutable base::Lock lock_;
// Only stores valid configs. |absl::nullopt| if most recent config was
// invalid (or no valid config has yet been read).
absl::optional<DnsConfig> config_;
std::map<Observer*, std::unique_ptr<WrappedObserver>> wrapped_observers_;
// Fields valid only on |task_runner_|.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
std::unique_ptr<DnsConfigService> dns_config_service_;
base::WeakPtrFactory<Core> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(Core);
};
SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier()
: SystemDnsConfigChangeNotifier(
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}),
DnsConfigService::CreateSystemService()) {}
SystemDnsConfigChangeNotifier::SystemDnsConfigChangeNotifier(
scoped_refptr<base::SequencedTaskRunner> task_runner,
std::unique_ptr<DnsConfigService> dns_config_service)
: core_(nullptr, base::OnTaskRunnerDeleter(task_runner)) {
if (dns_config_service)
core_.reset(new Core(task_runner, std::move(dns_config_service)));
}
SystemDnsConfigChangeNotifier::~SystemDnsConfigChangeNotifier() = default;
void SystemDnsConfigChangeNotifier::AddObserver(Observer* observer) {
if (core_)
core_->AddObserver(observer);
}
void SystemDnsConfigChangeNotifier::RemoveObserver(Observer* observer) {
if (core_)
core_->RemoveObserver(observer);
}
void SystemDnsConfigChangeNotifier::RefreshConfig() {
if (core_)
core_->RefreshConfig();
}
void SystemDnsConfigChangeNotifier::SetDnsConfigServiceForTesting(
std::unique_ptr<DnsConfigService> dns_config_service) {
DCHECK(core_);
DCHECK(dns_config_service);
core_->SetDnsConfigServiceForTesting(std::move(dns_config_service));
}
} // namespace net