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{}) : set(way_size, {}, equal) {}
28 ~MutexDatum() {
UASSERT_MSG(set.empty(),
"MutexDatum is destroyed while someone is holding the lock"); }
31 engine::ConditionVariable cv;
32 std::unordered_set<T, std::hash<T>, Equal> set;
45template <
typename Key,
typename Equal>
46class ItemMutex final {
48 using HashAndKey =
utils::CachedHash<Key>;
50 using MutexDatum = impl::MutexDatum<HashAndKey, utils::CachedHashKeyEqual<Equal>>;
52 ItemMutex(MutexDatum& md, HashAndKey&& key);
60 template <
typename Rep,
typename Period>
61 bool try_lock_for(std::chrono::duration<Rep, Period>);
63 template <
typename Clock,
typename Duration>
64 bool try_lock_until(std::chrono::time_point<Clock, Duration>);
67 bool TryFinishLocking();
70 const HashAndKey key_;
83template <
typename Key = std::string,
typename Hash = std::hash<Key>,
typename Equal = std::equal_to<Key>>
84class MutexSet final : Hash {
86 explicit MutexSet(size_t ways = 1, size_t way_size = 1,
const Hash& hash = Hash{},
const Equal& equal = Equal{});
94 using MutexDatum =
typename ItemMutex<Key, Equal>::MutexDatum;
95 utils::FixedArray<MutexDatum> mutex_data_;
98template <
typename Key,
typename Hash,
typename Equal>
99MutexSet<Key, Hash, Equal>::MutexSet(size_t ways, size_t way_size,
const Hash& hash,
const Equal& equal)
100 : Hash(hash), mutex_data_(ways, way_size, utils::CachedHashKeyEqual<Equal>{equal}) {}
102template <
typename Key,
typename Hash,
typename Equal>
104 const auto hash_value = Hash::operator()(key);
105 const auto size = mutex_data_.size();
108 const auto way = hash_value % size;
109 const auto new_hash = hash_value / size;
111 return ItemMutex<Key, Equal>(mutex_data_[way], {new_hash, std::move(key)});
114template <
typename Key,
typename Equal>
115ItemMutex<Key, Equal>::ItemMutex(MutexDatum& md, HashAndKey&& key) : md_(md), key_(std::move(key)) {}
117template <
typename Key,
typename Equal>
118void ItemMutex<Key, Equal>::lock() {
119 engine::TaskCancellationBlocker blocker;
120 std::unique_lock<engine::Mutex> lock(md_.mutex);
122 [[maybe_unused]]
auto is_locked = md_.cv.Wait(lock, [
this] {
return TryFinishLocking(); });
126template <
typename Key,
typename Equal>
127void ItemMutex<Key, Equal>::unlock() {
128 std::unique_lock lock(md_.mutex);
131 [[maybe_unused]]
auto node = md_.set.extract(key_);
150template <
typename Key,
typename Equal>
151bool ItemMutex<Key, Equal>::try_lock() {
152 std::unique_lock<engine::Mutex> lock(md_.mutex);
153 return TryFinishLocking();
156template <
typename Key,
typename Equal>
157template <
typename Rep,
typename Period>
158bool ItemMutex<Key, Equal>::try_lock_for(std::chrono::duration<Rep, Period> duration) {
159 std::unique_lock<engine::Mutex> lock(md_.mutex);
160 return md_.cv.WaitFor(lock, duration, [
this] {
return TryFinishLocking(); });
163template <
typename Key,
typename Equal>
164template <
typename Clock,
typename Duration>
165bool ItemMutex<Key, Equal>::try_lock_until(std::chrono::time_point<Clock, Duration> time_point) {
166 std::unique_lock<engine::Mutex> lock(md_.mutex);
167 return md_.cv.WaitUntil(lock, time_point, [
this] {
return TryFinishLocking(); });
170template <
typename Key,
typename Equal>
171bool ItemMutex<Key, Equal>::TryFinishLocking() {
172 return md_.set.insert(key_).second;