1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// Copyright 2012 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/supports_user_data.h"
#include "base/auto_reset.h"
#include "base/feature_list.h"
#include "base/sequence_checker.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
namespace base {
struct SupportsUserData::Impl {
// Externally-defined data accessible by key.
absl::flat_hash_map<const void*, std::unique_ptr<Data>> user_data_;
};
std::unique_ptr<SupportsUserData::Data> SupportsUserData::Data::Clone() {
return nullptr;
}
SupportsUserData::SupportsUserData() : impl_(std::make_unique<Impl>()) {
// Harmless to construct on a different execution sequence to subsequent
// usage.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
SupportsUserData::SupportsUserData(SupportsUserData&& rhs) {
*this = std::move(rhs);
}
SupportsUserData& SupportsUserData::operator=(SupportsUserData&& rhs) {
CHECK(!in_clear_);
CHECK(!rhs.in_clear_);
impl_ = std::move(rhs.impl_);
// No need to set `in_clear_` since it must be `false`.
rhs.impl_ = std::make_unique<Impl>();
return *this;
}
SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Avoid null keys; they are too vulnerable to collision.
DCHECK(key);
auto found = impl_->user_data_.find(key);
if (found != impl_->user_data_.end()) {
return found->second.get();
}
return nullptr;
}
std::unique_ptr<SupportsUserData::Data> SupportsUserData::TakeUserData(
const void* key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Null keys are too vulnerable to collision.
CHECK(key);
auto found = impl_->user_data_.find(key);
if (found != impl_->user_data_.end()) {
std::unique_ptr<SupportsUserData::Data> deowned;
deowned.swap(found->second);
impl_->user_data_.erase(key);
return deowned;
}
return nullptr;
}
void SupportsUserData::SetUserData(const void* key,
std::unique_ptr<Data> data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!in_clear_) << "Calling SetUserData() when SupportsUserData is "
"being cleared or destroyed is not supported.";
// Avoid null keys; they are too vulnerable to collision.
DCHECK(key);
if (data.get()) {
impl_->user_data_[key] = std::move(data);
} else {
RemoveUserData(key);
}
}
void SupportsUserData::RemoveUserData(const void* key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = impl_->user_data_.find(key);
if (it != impl_->user_data_.end()) {
// Remove the entry from the map before deleting `owned_data` to avoid
// reentrancy issues when `owned_data` owns `this`. Otherwise:
//
// 1. `RemoveUserData()` calls `erase()`.
// 2. `erase()` deletes `owned_data`.
// 3. `owned_data` deletes `this`.
//
// At this point, `erase()` is still on the stack even though the
// backing map (owned by `this`) has already been destroyed, and it
// may simply crash, cause a use-after-free, or any other number of
// interesting things.
auto owned_data = std::move(it->second);
impl_->user_data_.erase(it);
}
}
void SupportsUserData::DetachFromSequence() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
void SupportsUserData::CloneDataFrom(const SupportsUserData& other) {
CHECK(!in_clear_);
CHECK(!other.in_clear_);
for (const auto& data_pair : other.impl_->user_data_) {
auto cloned_data = data_pair.second->Clone();
if (cloned_data) {
SetUserData(data_pair.first, std::move(cloned_data));
}
}
}
SupportsUserData::~SupportsUserData() {
if (!impl_->user_data_.empty()) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
CHECK(!in_clear_);
in_clear_ = true;
// Swapping to a local variable to clear the entries serves two purposes:
// - `this` is in a consistent state if `SupportsUserData::Data` instances
// attempt to call back into the `SupportsUserData` during destruction.
// - `SupportsUserData::Data` instances cannot reference each other during
// destruction, which is desirable since destruction order of the
// `SupportsUserData::Data` instances is non-deterministic.
absl::flat_hash_map<const void*, std::unique_ptr<Data>> user_data;
impl_->user_data_.swap(user_data);
}
void SupportsUserData::ClearAllUserData() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If another clear operation is in progress, that means weird reentrancy of
// some sort.
CHECK(!in_clear_);
base::AutoReset<bool> reset_in_clear(&in_clear_, true);
// For similar reasons to the destructor, clear the entries by swapping to a
// local.
absl::flat_hash_map<const void*, std::unique_ptr<Data>> user_data;
impl_->user_data_.swap(user_data);
}
} // namespace base
|