9#include <unordered_set>
12#include <userver/engine/condition_variable.hpp>
13#include <userver/engine/mutex.hpp>
14#include <userver/engine/task/cancel.hpp>
15#include <userver/utils/cached_hash.hpp>
16#include <userver/utils/fixed_array.hpp>
18USERVER_NAMESPACE_BEGIN
24template <
typename T,
typename Equal>
25struct MutexDatum final {
26 explicit MutexDatum(size_t way_size,
const Equal& equal = Equal{})
27 : set(way_size, {}, equal) {}
31 "MutexDatum is destroyed while someone is holding the lock");
35 engine::ConditionVariable cv;
36 std::unordered_set<T, std::hash<T>, Equal> set;
49template <
typename Key,
typename Equal>
50class ItemMutex final {
52 using HashAndKey = utils::CachedHash<Key>;
55 impl::MutexDatum<HashAndKey, utils::CachedHashKeyEqual<Equal>>;
57 ItemMutex(MutexDatum& md, HashAndKey&& key);
65 template <
typename Rep,
typename Period>
66 bool try_lock_for(std::chrono::duration<Rep, Period>);
68 template <
typename Clock,
typename Duration>
69 bool try_lock_until(std::chrono::time_point<Clock, Duration>);
72 bool TryFinishLocking();
75 const HashAndKey key_;
88template <
typename Key = std::string,
typename Hash = std::hash<Key>,
89 typename Equal = std::equal_to<Key>>
90class MutexSet final : Hash {
92 explicit MutexSet(size_t ways = 1, size_t way_size = 1,
93 const Hash& hash = Hash{},
const Equal& equal = Equal{});
101 using MutexDatum =
typename ItemMutex<Key, Equal>::MutexDatum;
102 utils::FixedArray<MutexDatum> mutex_data_;
105template <
typename Key,
typename Hash,
typename Equal>
106MutexSet<Key, Hash, Equal>::MutexSet(size_t ways, size_t way_size,
107 const Hash& hash,
const Equal& equal)
109 mutex_data_(ways, way_size, utils::CachedHashKeyEqual<Equal>{equal}) {}
111template <
typename Key,
typename Hash,
typename Equal>
113 const auto hash_value = Hash::operator()(key);
114 const auto size = mutex_data_.size();
117 const auto way = hash_value % size;
118 const auto new_hash = hash_value / size;
120 return ItemMutex<Key, Equal>(mutex_data_[way], {new_hash, std::move(key)});
123template <
typename Key,
typename Equal>
124ItemMutex<Key, Equal>::ItemMutex(MutexDatum& md, HashAndKey&& key)
125 : md_(md), key_(std::move(key)) {}
127template <
typename Key,
typename Equal>
128void ItemMutex<Key, Equal>::lock() {
129 engine::TaskCancellationBlocker blocker;
130 std::unique_lock<engine::Mutex> lock(md_.mutex);
132 [[maybe_unused]]
auto is_locked =
133 md_.cv.Wait(lock, [
this] {
return TryFinishLocking(); });
137template <
typename Key,
typename Equal>
138void ItemMutex<Key, Equal>::unlock() {
139 std::unique_lock lock(md_.mutex);
142 [[maybe_unused]]
auto node = md_.set.extract(key_);
161template <
typename Key,
typename Equal>
162bool ItemMutex<Key, Equal>::try_lock() {
163 std::unique_lock<engine::Mutex> lock(md_.mutex);
164 return TryFinishLocking();
167template <
typename Key,
typename Equal>
168template <
typename Rep,
typename Period>
169bool ItemMutex<Key, Equal>::try_lock_for(
170 std::chrono::duration<Rep, Period> duration) {
171 std::unique_lock<engine::Mutex> lock(md_.mutex);
172 return md_.cv.WaitFor(lock, duration, [
this] {
return TryFinishLocking(); });
175template <
typename Key,
typename Equal>
176template <
typename Clock,
typename Duration>
177bool ItemMutex<Key, Equal>::try_lock_until(
178 std::chrono::time_point<Clock, Duration> time_point) {
179 std::unique_lock<engine::Mutex> lock(md_.mutex);
180 return md_.cv.WaitUntil(lock, time_point,
181 [
this] {
return TryFinishLocking(); });
184template <
typename Key,
typename Equal>
185bool ItemMutex<Key, Equal>::TryFinishLocking() {
186 return md_.set.insert(key_).second;