10#include <unordered_map> 
   13#include <userver/rcu/rcu.hpp> 
   14#include <userver/utils/traceful_exception.hpp> 
   16USERVER_NAMESPACE_BEGIN
 
   21template <
typename RcuMapTraits>
 
   22struct RcuTraitsFromRcuMapTraits {
 
   23  using MutexType = 
typename RcuMapTraits::MutexType;
 
   40template <
typename Key, 
typename Value>
 
   41struct DefaultRcuMapTraits {
 
   42  using Hash = std::hash<Key>;
 
   43  using KeyEqual = std::equal_to<Key>;
 
   44  using MutexType = engine::Mutex;
 
   50template <
typename Key, 
typename Value, 
typename IterValue,
 
   51          typename RcuMapTraits>
 
   52class RcuMapIterator final {
 
   53  using Hash = 
typename RcuMapTraits::Hash;
 
   54  using KeyEqual = 
typename RcuMapTraits::KeyEqual;
 
   56      std::unordered_map<Key, std::shared_ptr<Value>, Hash, KeyEqual>;
 
   57  using BaseIterator = 
typename MapType::const_iterator;
 
   58  using RcuTraits = 
typename impl::RcuTraitsFromRcuMapTraits<RcuMapTraits>;
 
   61  using iterator_category = std::input_iterator_tag;
 
   62  using difference_type = ptrdiff_t;
 
   63  using value_type = std::pair<Key, std::shared_ptr<IterValue>>;
 
   64  using reference = 
const value_type&;
 
   65  using pointer = 
const value_type*;
 
   67  RcuMapIterator() = 
default;
 
   69  RcuMapIterator operator++(
int);
 
   70  RcuMapIterator& operator++();
 
   71  reference operator*() 
const;
 
   72  pointer operator->() 
const;
 
   74  bool operator==(
const RcuMapIterator&) 
const;
 
   75  bool operator!=(
const RcuMapIterator&) 
const;
 
   79  RcuMapIterator(ReadablePtr<MapType, RcuTraits>&& ptr, BaseIterator iter);
 
   85  std::optional<ReadablePtr<MapType, RcuTraits>> ptr_;
 
  106template <
typename Key, 
typename Value, 
typename RcuMapTraits>
 
  108  using RcuTraits = 
typename impl::RcuTraitsFromRcuMapTraits<RcuMapTraits>;
 
  111  static_assert(!std::is_reference_v<Key>);
 
  112  static_assert(!std::is_reference_v<Value>);
 
  113  static_assert(!std::is_const_v<Key>);
 
  115  template <
typename ValuePtrType>
 
  116  struct InsertReturnTypeImpl;
 
  118  using Hash = 
typename RcuMapTraits::Hash;
 
  119  using KeyEqual = 
typename RcuMapTraits::KeyEqual;
 
  120  using MutexType = 
typename RcuMapTraits::MutexType;
 
  121  using ValuePtr = std::shared_ptr<Value>;
 
  122  using Iterator = RcuMapIterator<Key, Value, Value, RcuMapTraits>;
 
  123  using ConstValuePtr = std::shared_ptr<
const Value>;
 
  124  using ConstIterator = RcuMapIterator<Key, Value, 
const Value, RcuMapTraits>;
 
  125  using RawMap = std::unordered_map<Key, ValuePtr, Hash, KeyEqual>;
 
  126  using Snapshot = std::unordered_map<Key, ConstValuePtr, Hash, KeyEqual>;
 
  127  using InsertReturnType = InsertReturnTypeImpl<ValuePtr>;
 
  131  RcuMap(
const RcuMap&) = 
delete;
 
  132  RcuMap(RcuMap&&) = 
delete;
 
  133  RcuMap& operator=(
const RcuMap&) = 
delete;
 
  134  RcuMap& operator=(RcuMap&&) = 
delete;
 
  143  ConstIterator begin() 
const;
 
  144  ConstIterator end() 
const;
 
  164  InsertReturnType 
Insert(
const Key& key, ValuePtr value);
 
  172  template <
typename... Args>
 
  173  InsertReturnType 
Emplace(
const Key& key, Args&&... args);
 
  184  template <
typename... Args>
 
  189  template <
typename RawKey>
 
  193  const ConstValuePtr 
Get(
const Key&) 
const;
 
  196  const ValuePtr 
Get(
const Key&);
 
  225  InsertReturnType DoInsert(
const Key& key, ValuePtr value);
 
  227  rcu::Variable<RawMap, RcuTraits> rcu_;
 
  231template <
typename ValuePtrType>
 
  232struct RcuMap<K, V, RcuMapTraits>::InsertReturnTypeImpl {
 
  237template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  238typename RcuMap<K, V, RcuMapTraits>::ConstIterator
 
  239RcuMap<K, V, RcuMapTraits>::begin() 
const {
 
  240  auto ptr = rcu_.Read();
 
  241  const auto iter = ptr->cbegin();
 
  243      typename RcuMap<K, V, RcuMapTraits>::ConstIterator(std::move(ptr), iter);
 
  246template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  247typename RcuMap<K, V, RcuMapTraits>::ConstIterator
 
  248RcuMap<K, V, RcuMapTraits>::end() 
const {
 
  254template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  255typename RcuMap<K, V, RcuMapTraits>::Iterator
 
  256RcuMap<K, V, RcuMapTraits>::begin() {
 
  257  auto ptr = rcu_.Read();
 
  258  const auto iter = ptr->cbegin();
 
  259  return {std::move(ptr), iter};
 
  262template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  263typename RcuMap<K, V, RcuMapTraits>::Iterator
 
  264RcuMap<K, V, RcuMapTraits>::end() {
 
  270template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  272  auto ptr = rcu_.Read();
 
  276template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  279const typename RcuMap<K, V, RcuMapTraits>::ConstValuePtr
 
  280RcuMap<K, V, RcuMapTraits>::
operator[](
const K& key) 
const {
 
  281  if (
auto value = Get(key)) {
 
  284  throw MissingKeyException(
"Key ") << key << 
" is missing";
 
  287template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  290const typename RcuMap<K, V, RcuMapTraits>::ConstValuePtr
 
  291RcuMap<K, V, RcuMapTraits>::
Get(
const K& key) 
const {
 
  293  return const_cast<RcuMap<K, V, RcuMapTraits>*>(
this)->Get(key);
 
  296template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  299const typename RcuMap<K, V, RcuMapTraits>::ValuePtr
 
  300RcuMap<K, V, RcuMapTraits>::
operator[](
const K& key) {
 
  301  auto value = Get(key);
 
  303    auto txn = rcu_.StartWrite();
 
  304    auto insertion_result = txn->emplace(key, std::make_shared<V>());
 
  305    value = insertion_result.first->second;
 
  306    if (insertion_result.second) txn.Commit();
 
  311template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  312typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
 
  314    const K& key, 
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
 
  315  InsertReturnType result{Get(key), 
false};
 
  316  if (result.value) 
return result;
 
  318  return DoInsert(key, std::move(value));
 
  322template <
typename... Args>
 
  323typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
 
  324RcuMap<K, V, RcuMapTraits>::
Emplace(
const K& key, Args&&... args) {
 
  325  InsertReturnType result{Get(key), 
false};
 
  326  if (result.value) 
return result;
 
  328  return DoInsert(key, std::make_shared<V>(std::forward<Args>(args)...));
 
  331template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  332typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
 
  333RcuMap<K, V, RcuMapTraits>::DoInsert(
 
  334    const K& key, 
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
 
  335  auto txn = rcu_.StartWrite();
 
  336  auto insertion_result = txn->emplace(key, std::move(value));
 
  337  InsertReturnType result{insertion_result.first->second,
 
  338                          insertion_result.second};
 
  339  if (result.inserted) txn.Commit();
 
  344template <
typename... Args>
 
  345typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
 
  346RcuMap<K, V, RcuMapTraits>::
TryEmplace(
const K& key, Args&&... args) {
 
  347  InsertReturnType result{Get(key), 
false};
 
  349    auto txn = rcu_.StartWrite();
 
  350    auto insertion_result = txn->try_emplace(key, 
nullptr);
 
  351    if (insertion_result.second) {
 
  352      result.value = insertion_result.first->second =
 
  353          std::make_shared<V>(std::forward<Args>(args)...);
 
  355      result.inserted = 
true;
 
  357      result.value = insertion_result.first->second;
 
  364template <
typename RawKey>
 
  366                                                      RcuMap::ValuePtr value) {
 
  367  auto txn = rcu_.StartWrite();
 
  368  txn->insert_or_assign(std::forward<RawKey>(key), std::move(value));
 
  372template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  375const typename RcuMap<K, V, RcuMapTraits>::ValuePtr
 
  376RcuMap<K, V, RcuMapTraits>::
Get(
const K& key) {
 
  377  auto snapshot = rcu_.Read();
 
  378  auto it = snapshot->find(key);
 
  379  if (it == snapshot->end()) 
return {};
 
  383template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  384bool RcuMap<K, V, RcuMapTraits>::
Erase(
const K& key) {
 
  386    auto txn = rcu_.StartWrite();
 
  387    if (txn->erase(key)) {
 
  395template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  396typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<K, V, RcuMapTraits>::
Pop(
 
  398  auto value = Get(key);
 
  400    auto txn = rcu_.StartWrite();
 
  401    if (txn->erase(key)) txn.Commit();
 
  406template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  407void RcuMap<K, V, RcuMapTraits>::
Clear() {
 
  411template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  412void RcuMap<K, V, RcuMapTraits>::
Assign(RawMap new_map) {
 
  413  rcu_.Assign(std::move(new_map));
 
  416template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  419  return rcu_.StartWrite();
 
  422template <
typename K, 
typename V, 
typename RcuMapTraits>
 
  425  return {begin(), end()};
 
  428template <
typename Key, 
typename Value, 
typename IterValue,
 
  429          typename RcuMapTraits>
 
  430RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::RcuMapIterator(
 
  431    ReadablePtr<MapType, RcuTraits>&& ptr,
 
  432    typename MapType::const_iterator iter)
 
  433    : ptr_(std::move(ptr)), it_(iter) {
 
  437template <
typename Key, 
typename Value, 
typename IterValue,
 
  438          typename RcuMapTraits>
 
  439auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++(
int)
 
  441  RcuMapIterator tmp(*
this);
 
  446template <
typename Key, 
typename Value, 
typename IterValue,
 
  447          typename RcuMapTraits>
 
  448auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++()
 
  455template <
typename Key, 
typename Value, 
typename IterValue,
 
  456          typename RcuMapTraits>
 
  457auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator*() 
const 
  462template <
typename Key, 
typename Value, 
typename IterValue,
 
  463          typename RcuMapTraits>
 
  464auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator->() 
const 
  469template <
typename Key, 
typename Value, 
typename IterValue,
 
  470          typename RcuMapTraits>
 
  471bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator==(
 
  472    const RcuMapIterator& rhs) 
const {
 
  475      return it_ == rhs.it_;
 
  477      return it_ == (*ptr_)->end();
 
  480    return !rhs.ptr_ || rhs.it_ == (*rhs.ptr_)->end();
 
  484template <
typename Key, 
typename Value, 
typename IterValue,
 
  485          typename RcuMapTraits>
 
  486bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator!=(
 
  487    const RcuMapIterator& rhs) 
const {
 
  488  return !(*
this == rhs);
 
  491template <
typename Key, 
typename Value, 
typename IterValue,
 
  492          typename RcuMapTraits>
 
  493void RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::UpdateCurrent() {
 
  494  if (it_ != (*ptr_)->end()) {