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;