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