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()) {