10#include <unordered_map>
13#include <userver/rcu/rcu.hpp>
14#include <userver/utils/traceful_exception.hpp>
16USERVER_NAMESPACE_BEGIN
22template <
typename RcuMapTraits>
24 using MutexType =
typename RcuMapTraits::MutexType;
25 using DeleterType =
typename RcuMapTraits::DeleterType;
28struct ShouldInheritFromDefaultRcuMapTraits {};
35 using utils::TracefulException::TracefulException;
45template <
typename Key>
46struct DefaultRcuMapTraits :
public impl::ShouldInheritFromDefaultRcuMapTraits {
47 using Hash = std::hash<Key>;
48 using KeyEqual = std::equal_to<Key>;
49 using MutexType = engine::Mutex;
50 using DeleterType = AsyncDeleter;
56template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
57class RcuMapIterator final {
59 std::is_base_of_v<impl::ShouldInheritFromDefaultRcuMapTraits, RcuMapTraits>,
60 "RcuMapTraits should inherit from rcu::DefaultRcuMapTraits"
62 using Hash =
typename RcuMapTraits::Hash;
63 using KeyEqual =
typename RcuMapTraits::KeyEqual;
64 using MapType = std::unordered_map<Key, std::shared_ptr<Value>, Hash, KeyEqual>;
65 using BaseIterator =
typename MapType::const_iterator;
66 using RcuTraits =
typename impl::RcuTraitsFromRcuMapTraits<RcuMapTraits>;
69 using iterator_category = std::input_iterator_tag;
70 using difference_type = ptrdiff_t;
71 using value_type = std::pair<Key, std::shared_ptr<IterValue>>;
72 using reference =
const value_type&;
73 using pointer =
const value_type*;
75 RcuMapIterator() =
default;
77 RcuMapIterator operator++(
int);
78 RcuMapIterator& operator++();
79 reference operator*()
const;
80 pointer operator->()
const;
82 bool operator==(
const RcuMapIterator&)
const;
83 bool operator!=(
const RcuMapIterator&)
const;
87 RcuMapIterator(ReadablePtr<MapType, RcuTraits>&& ptr, BaseIterator iter);
93 std::optional<ReadablePtr<MapType, RcuTraits>> ptr_;
114template <
typename Key,
typename Value,
typename RcuMapTraits>
116 using RcuTraits =
typename impl::RcuTraitsFromRcuMapTraits<RcuMapTraits>;
119 static_assert(!std::is_reference_v<Key>);
120 static_assert(!std::is_reference_v<Value>);
121 static_assert(!std::is_const_v<Key>);
123 template <
typename ValuePtrType>
124 struct InsertReturnTypeImpl;
126 using Hash =
typename RcuMapTraits::Hash;
127 using KeyEqual =
typename RcuMapTraits::KeyEqual;
128 using MutexType =
typename RcuMapTraits::MutexType;
129 using ValuePtr = std::shared_ptr<Value>;
130 using Iterator = RcuMapIterator<Key, Value, Value, RcuMapTraits>;
131 using ConstValuePtr = std::shared_ptr<
const Value>;
132 using ConstIterator = RcuMapIterator<Key, Value,
const Value, RcuMapTraits>;
133 using RawMap = std::unordered_map<Key, ValuePtr, Hash, KeyEqual>;
134 using Snapshot = std::unordered_map<Key, ConstValuePtr, Hash, KeyEqual>;
135 using InsertReturnType = InsertReturnTypeImpl<ValuePtr>;
139 RcuMap(
const RcuMap&) =
delete;
140 RcuMap(RcuMap&&) =
delete;
141 RcuMap& operator=(
const RcuMap&) =
delete;
142 RcuMap& operator=(RcuMap&&) =
delete;
151 ConstIterator begin()
const;
152 ConstIterator end()
const;
172 InsertReturnType
Insert(
const Key& key, ValuePtr value);
180 template <
typename... Args>
181 InsertReturnType
Emplace(
const Key& key, Args&&... args);
192 template <
typename... Args>
197 template <
typename RawKey>
201 const ConstValuePtr
Get(
const Key&)
const;
204 const ValuePtr
Get(
const Key&);
233 InsertReturnType DoInsert(
const Key& key, ValuePtr value);
235 rcu::Variable<RawMap, RcuTraits> rcu_;
239template <
typename ValuePtrType>
240struct RcuMap<K, V, RcuMapTraits>::InsertReturnTypeImpl {
245template <
typename K,
typename V,
typename RcuMapTraits>
246typename RcuMap<K, V, RcuMapTraits>::ConstIterator RcuMap<K, V, RcuMapTraits>::begin()
const {
247 auto ptr = rcu_.Read();
248 const auto iter = ptr->cbegin();
249 return typename RcuMap<K, V, RcuMapTraits>::ConstIterator(std::move(ptr), iter);
252template <
typename K,
typename V,
typename RcuMapTraits>
253typename RcuMap<K, V, RcuMapTraits>::ConstIterator RcuMap<K, V, RcuMapTraits>::end()
const {
259template <
typename K,
typename V,
typename RcuMapTraits>
260typename RcuMap<K, V, RcuMapTraits>::Iterator RcuMap<K, V, RcuMapTraits>::begin() {
261 auto ptr = rcu_.Read();
262 const auto iter = ptr->cbegin();
263 return {std::move(ptr), iter};
266template <
typename K,
typename V,
typename RcuMapTraits>
267typename RcuMap<K, V, RcuMapTraits>::Iterator RcuMap<K, V, RcuMapTraits>::end() {
273template <
typename K,
typename V,
typename RcuMapTraits>
275 auto ptr = rcu_.Read();
279template <
typename K,
typename V,
typename RcuMapTraits>
282const typename RcuMap<K, V, RcuMapTraits>::ConstValuePtr RcuMap<K, V, RcuMapTraits>::
operator[](
const K& key)
const {
283 if (
auto value = Get(key)) {
289template <
typename K,
typename V,
typename RcuMapTraits>
292const typename RcuMap<K, V, RcuMapTraits>::ConstValuePtr RcuMap<K, V, RcuMapTraits>::
Get(
const K& key)
const {
294 return const_cast<RcuMap<K, V, RcuMapTraits>*>(
this)->Get(key);
297template <
typename K,
typename V,
typename RcuMapTraits>
300const typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<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) {
313template <
typename K,
typename V,
typename RcuMapTraits>
314typename RcuMap<K, V, RcuMapTraits>::InsertReturnType RcuMap<
317 RcuMapTraits>::
Insert(
const K& key,
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
318 InsertReturnType result{Get(key),
false};
323 return DoInsert(key, std::move(value));
327template <
typename... Args>
328typename RcuMap<K, V, RcuMapTraits>::InsertReturnType RcuMap<
331 RcuMapTraits>::
Emplace(
const K& key, Args&&... args) {
332 InsertReturnType result{Get(key),
false};
337 return DoInsert(key, std::make_shared<V>(std::forward<Args>(args)...));
340template <
typename K,
typename V,
typename RcuMapTraits>
341typename RcuMap<K, V, RcuMapTraits>::InsertReturnType RcuMap<
344 RcuMapTraits>::DoInsert(
const K& key,
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
345 auto txn = rcu_.StartWrite();
346 auto insertion_result = txn->emplace(key, std::move(value));
347 InsertReturnType result{insertion_result.first->second, insertion_result.second};
348 if (result.inserted) {
355template <
typename... Args>
356typename RcuMap<K, V, RcuMapTraits>::InsertReturnType RcuMap<
359 RcuMapTraits>::
TryEmplace(
const K& key, Args&&... args) {
360 InsertReturnType result{Get(key),
false};
362 auto txn = rcu_.StartWrite();
363 auto insertion_result = txn->try_emplace(key,
nullptr);
364 if (insertion_result.second) {
365 result.value = insertion_result.first->second = std::make_shared<V>(std::forward<Args>(args)...);
367 result.inserted =
true;
369 result.value = insertion_result.first->second;
376template <
typename RawKey>
377void RcuMap<Key, Value, RcuMapTraits>::
InsertOrAssign(RawKey&& key, RcuMap::ValuePtr value) {
378 auto txn = rcu_.StartWrite();
379 txn->insert_or_assign(std::forward<RawKey>(key), std::move(value));
383template <
typename K,
typename V,
typename RcuMapTraits>
386const typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<K, V, RcuMapTraits>::
Get(
const K& key) {
387 auto snapshot = rcu_.Read();
388 auto it = snapshot->find(key);
389 if (it == snapshot->end()) {
395template <
typename K,
typename V,
typename RcuMapTraits>
396bool RcuMap<K, V, RcuMapTraits>::
Erase(
const K& key) {
398 auto txn = rcu_.StartWrite();
399 if (txn->erase(key)) {
407template <
typename K,
typename V,
typename RcuMapTraits>
408typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<K, V, RcuMapTraits>::
Pop(
const K& key) {
409 auto value = Get(key);
411 auto txn = rcu_.StartWrite();
412 if (txn->erase(key)) {
419template <
typename K,
typename V,
typename RcuMapTraits>
420void RcuMap<K, V, RcuMapTraits>::
Clear() {
424template <
typename K,
typename V,
typename RcuMapTraits>
425void RcuMap<K, V, RcuMapTraits>::
Assign(RawMap new_map) {
426 rcu_.Assign(std::move(new_map));
429template <
typename K,
typename V,
typename RcuMapTraits>
430auto RcuMap<K, V, RcuMapTraits>::
StartWrite() ->
rcu::WritablePtr<RawMap, RcuTraits> {
431 return rcu_.StartWrite();
434template <
typename K,
typename V,
typename RcuMapTraits>
435typename RcuMap<K, V, RcuMapTraits>::Snapshot RcuMap<K, V, RcuMapTraits>::
GetSnapshot()
const {
436 return {begin(), end()};
439template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
444 RcuMapTraits>::RcuMapIterator(ReadablePtr<MapType, RcuTraits>&& ptr,
typename MapType::const_iterator iter)
445 : ptr_(std::move(ptr)),
451template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
452auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++(
int) -> RcuMapIterator {
453 RcuMapIterator tmp(*
this);
458template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
459auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++() -> RcuMapIterator& {
465template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
466auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator*()
const -> reference {
470template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
471auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator->()
const -> pointer {
475template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
476bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator==(
const RcuMapIterator& rhs)
const {
479 return it_ == rhs.it_;
481 return it_ == (*ptr_)->end();
484 return !rhs.ptr_ || rhs.it_ == (*rhs.ptr_)->end();
488template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
489bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator!=(
const RcuMapIterator& rhs)
const {
490 return !(*
this == rhs);
493template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
494void RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::UpdateCurrent() {
495 if (it_ != (*ptr_)->end()) {