blob: aa0e0ae0da4583348b5a264469c835d5f6586b4d [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/allocator/dispatcher/dispatcher.h"
#include "base/allocator/buildflags.h"
#include "base/allocator/dispatcher/internal/dispatch_data.h"
#if !defined(COBALT_PENDING_CLEAN_UP)
#include "base/allocator/partition_allocator/partition_alloc.h"
#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
#endif
#include "base/check.h"
#include "base/dcheck_is_on.h"
#include "base/no_destructor.h"
#if DCHECK_IS_ON()
#include <atomic>
#endif
namespace base::allocator::dispatcher {
// The private implementation of Dispatcher.
struct Dispatcher::Impl {
void Initialize(const internal::DispatchData& dispatch_data) {
#if DCHECK_IS_ON()
DCHECK(!is_initialized_check_flag_.test_and_set());
#endif
dispatch_data_ = dispatch_data;
ConnectToEmitters(dispatch_data_);
}
void Reset() {
#if DCHECK_IS_ON()
DCHECK([&]() {
auto const was_set = is_initialized_check_flag_.test_and_set();
is_initialized_check_flag_.clear();
return was_set;
}());
#endif
DisconnectFromEmitters(dispatch_data_);
dispatch_data_ = {};
}
private:
// Connect the hooks to the memory subsystem. In some cases, most notably when
// we have no observers at all, the hooks will be invalid and must NOT be
// connected. This way we prevent notifications although no observers are
// present.
static void ConnectToEmitters(const internal::DispatchData& dispatch_data) {
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
if (auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch()) {
allocator_shim::InsertAllocatorDispatch(allocator_dispatch);
}
#endif
#if BUILDFLAG(USE_PARTITION_ALLOC)
{
auto* const allocation_hook = dispatch_data.GetAllocationObserverHook();
auto* const free_hook = dispatch_data.GetFreeObserverHook();
if (allocation_hook && free_hook) {
partition_alloc::PartitionAllocHooks::SetObserverHooks(allocation_hook,
free_hook);
}
}
#endif
}
static void DisconnectFromEmitters(internal::DispatchData& dispatch_data) {
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
if (auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch()) {
allocator_shim::RemoveAllocatorDispatchForTesting(
allocator_dispatch); // IN-TEST
}
#endif
#if BUILDFLAG(USE_PARTITION_ALLOC)
partition_alloc::PartitionAllocHooks::SetObserverHooks(nullptr, nullptr);
#endif
}
// Information on the hooks.
internal::DispatchData dispatch_data_;
#if DCHECK_IS_ON()
// Indicator if the dispatcher has been initialized before.
#if !defined(__cpp_lib_atomic_value_initialization) || \
__cpp_lib_atomic_value_initialization < 201911L
std::atomic_flag is_initialized_check_flag_ = ATOMIC_FLAG_INIT;
#else
std::atomic_flag is_initialized_check_flag_;
#endif
#endif
};
Dispatcher::Dispatcher() : impl_(std::make_unique<Impl>()) {}
Dispatcher::~Dispatcher() = default;
Dispatcher& Dispatcher::GetInstance() {
static base::NoDestructor<Dispatcher> instance;
return *instance;
}
void Dispatcher::Initialize(const internal::DispatchData& dispatch_data) {
impl_->Initialize(dispatch_data);
}
void Dispatcher::ResetForTesting() {
impl_->Reset();
}
} // namespace base::allocator::dispatcher