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 {};
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)) {
286 throw MissingKeyException(
"Key ") << key <<
" is missing";
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) txn.Commit();
311template <
typename K,
typename V,
typename RcuMapTraits>
312typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
313RcuMap<K, V, RcuMapTraits>::
Insert(
const K& key,
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
314 InsertReturnType result{Get(key),
false};
315 if (result.value)
return result;
317 return DoInsert(key, std::move(value));
321template <
typename... Args>
322typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
323RcuMap<K, V, RcuMapTraits>::
Emplace(
const K& key, Args&&... args) {
324 InsertReturnType result{Get(key),
false};
325 if (result.value)
return result;
327 return DoInsert(key, std::make_shared<V>(std::forward<Args>(args)...));
330template <
typename K,
typename V,
typename RcuMapTraits>
331typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
332RcuMap<K, V, RcuMapTraits>::DoInsert(
const K& key,
typename RcuMap<K, V, RcuMapTraits>::ValuePtr value) {
333 auto txn = rcu_.StartWrite();
334 auto insertion_result = txn->emplace(key, std::move(value));
335 InsertReturnType result{insertion_result.first->second, insertion_result.second};
336 if (result.inserted) txn.Commit();
341template <
typename... Args>
342typename RcuMap<K, V, RcuMapTraits>::InsertReturnType
343RcuMap<K, V, RcuMapTraits>::
TryEmplace(
const K& key, Args&&... args) {
344 InsertReturnType result{Get(key),
false};
346 auto txn = rcu_.StartWrite();
347 auto insertion_result = txn->try_emplace(key,
nullptr);
348 if (insertion_result.second) {
349 result.value = insertion_result.first->second = std::make_shared<V>(std::forward<Args>(args)...);
351 result.inserted =
true;
353 result.value = insertion_result.first->second;
360template <
typename RawKey>
361void RcuMap<Key, Value, RcuMapTraits>::
InsertOrAssign(RawKey&& key, RcuMap::ValuePtr value) {
362 auto txn = rcu_.StartWrite();
363 txn->insert_or_assign(std::forward<RawKey>(key), std::move(value));
367template <
typename K,
typename V,
typename RcuMapTraits>
370const typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<K, V, RcuMapTraits>::
Get(
const K& key) {
371 auto snapshot = rcu_.Read();
372 auto it = snapshot->find(key);
373 if (it == snapshot->end())
return {};
377template <
typename K,
typename V,
typename RcuMapTraits>
378bool RcuMap<K, V, RcuMapTraits>::
Erase(
const K& key) {
380 auto txn = rcu_.StartWrite();
381 if (txn->erase(key)) {
389template <
typename K,
typename V,
typename RcuMapTraits>
390typename RcuMap<K, V, RcuMapTraits>::ValuePtr RcuMap<K, V, RcuMapTraits>::
Pop(
const K& key) {
391 auto value = Get(key);
393 auto txn = rcu_.StartWrite();
394 if (txn->erase(key)) txn.Commit();
399template <
typename K,
typename V,
typename RcuMapTraits>
400void RcuMap<K, V, RcuMapTraits>::
Clear() {
404template <
typename K,
typename V,
typename RcuMapTraits>
405void RcuMap<K, V, RcuMapTraits>::
Assign(RawMap new_map) {
406 rcu_.Assign(std::move(new_map));
409template <
typename K,
typename V,
typename RcuMapTraits>
411 return rcu_.StartWrite();
414template <
typename K,
typename V,
typename RcuMapTraits>
416 return {begin(), end()};
419template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
420RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::RcuMapIterator(
421 ReadablePtr<MapType, RcuTraits>&& ptr,
422 typename MapType::const_iterator iter
424 : ptr_(std::move(ptr)), it_(iter) {
428template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
429auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++(
int) -> RcuMapIterator {
430 RcuMapIterator tmp(*
this);
435template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
436auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator++() -> RcuMapIterator& {
442template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
443auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator*()
const -> reference {
447template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
448auto RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator->()
const -> pointer {
452template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
453bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator==(
const RcuMapIterator& rhs)
const {
456 return it_ == rhs.it_;
458 return it_ == (*ptr_)->end();
461 return !rhs.ptr_ || rhs.it_ == (*rhs.ptr_)->end();
465template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
466bool RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::operator!=(
const RcuMapIterator& rhs)
const {
467 return !(*
this == rhs);
470template <
typename Key,
typename Value,
typename IterValue,
typename RcuMapTraits>
471void RcuMapIterator<Key, Value, IterValue, RcuMapTraits>::UpdateCurrent() {
472 if (it_ != (*ptr_)->end()) {