• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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