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
base / supports_user_data.cc [blame]
// 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