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