hash_table_evictable.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright (c) 2013, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. #pragma once
  7. #ifndef ROCKSDB_LITE
  8. #include <functional>
  9. #include "util/random.h"
  10. #include "utilities/persistent_cache/hash_table.h"
  11. #include "utilities/persistent_cache/lrulist.h"
  12. namespace ROCKSDB_NAMESPACE {
  13. // Evictable Hash Table
  14. //
  15. // Hash table index where least accessed (or one of the least accessed) elements
  16. // can be evicted.
  17. //
  18. // Please note EvictableHashTable can only be created for pointer type objects
  19. template <class T, class Hash, class Equal>
  20. class EvictableHashTable : private HashTable<T*, Hash, Equal> {
  21. public:
  22. typedef HashTable<T*, Hash, Equal> hash_table;
  23. explicit EvictableHashTable(const size_t capacity = 1024 * 1024,
  24. const float load_factor = 2.0,
  25. const uint32_t nlocks = 256)
  26. : HashTable<T*, Hash, Equal>(capacity, load_factor, nlocks),
  27. lru_lists_(new LRUList<T>[hash_table::nlocks_]) {
  28. assert(lru_lists_);
  29. }
  30. virtual ~EvictableHashTable() { AssertEmptyLRU(); }
  31. //
  32. // Insert given record to hash table (and LRU list)
  33. //
  34. bool Insert(T* t) {
  35. const uint64_t h = Hash()(t);
  36. typename hash_table::Bucket& bucket = GetBucket(h);
  37. LRUListType& lru = GetLRUList(h);
  38. port::RWMutex& lock = GetMutex(h);
  39. WriteLock _(&lock);
  40. if (hash_table::Insert(&bucket, t)) {
  41. lru.Push(t);
  42. return true;
  43. }
  44. return false;
  45. }
  46. //
  47. // Lookup hash table
  48. //
  49. // Please note that read lock should be held by the caller. This is because
  50. // the caller owns the data, and should hold the read lock as long as he
  51. // operates on the data.
  52. bool Find(T* t, T** ret) {
  53. const uint64_t h = Hash()(t);
  54. typename hash_table::Bucket& bucket = GetBucket(h);
  55. LRUListType& lru = GetLRUList(h);
  56. port::RWMutex& lock = GetMutex(h);
  57. ReadLock _(&lock);
  58. if (hash_table::Find(&bucket, t, ret)) {
  59. ++(*ret)->refs_;
  60. lru.Touch(*ret);
  61. return true;
  62. }
  63. return false;
  64. }
  65. //
  66. // Evict one of the least recently used object
  67. //
  68. T* Evict(const std::function<void(T*)>& fn = nullptr) {
  69. uint32_t random = Random::GetTLSInstance()->Next();
  70. const size_t start_idx = random % hash_table::nlocks_;
  71. T* t = nullptr;
  72. // iterate from start_idx .. 0 .. start_idx
  73. for (size_t i = 0; !t && i < hash_table::nlocks_; ++i) {
  74. const size_t idx = (start_idx + i) % hash_table::nlocks_;
  75. WriteLock _(&hash_table::locks_[idx]);
  76. LRUListType& lru = lru_lists_[idx];
  77. if (!lru.IsEmpty() && (t = lru.Pop()) != nullptr) {
  78. assert(!t->refs_);
  79. // We got an item to evict, erase from the bucket
  80. const uint64_t h = Hash()(t);
  81. typename hash_table::Bucket& bucket = GetBucket(h);
  82. T* tmp = nullptr;
  83. bool status = hash_table::Erase(&bucket, t, &tmp);
  84. assert(t == tmp);
  85. (void)status;
  86. assert(status);
  87. if (fn) {
  88. fn(t);
  89. }
  90. break;
  91. }
  92. assert(!t);
  93. }
  94. return t;
  95. }
  96. void Clear(void (*fn)(T*)) {
  97. for (uint32_t i = 0; i < hash_table::nbuckets_; ++i) {
  98. const uint32_t lock_idx = i % hash_table::nlocks_;
  99. WriteLock _(&hash_table::locks_[lock_idx]);
  100. auto& lru_list = lru_lists_[lock_idx];
  101. auto& bucket = hash_table::buckets_[i];
  102. for (auto* t : bucket.list_) {
  103. lru_list.Unlink(t);
  104. (*fn)(t);
  105. }
  106. bucket.list_.clear();
  107. }
  108. // make sure that all LRU lists are emptied
  109. AssertEmptyLRU();
  110. }
  111. void AssertEmptyLRU() {
  112. #ifndef NDEBUG
  113. for (uint32_t i = 0; i < hash_table::nlocks_; ++i) {
  114. WriteLock _(&hash_table::locks_[i]);
  115. auto& lru_list = lru_lists_[i];
  116. assert(lru_list.IsEmpty());
  117. }
  118. #endif
  119. }
  120. //
  121. // Fetch the mutex associated with a key
  122. // This call is used to hold the lock for a given data for extended period of
  123. // time.
  124. port::RWMutex* GetMutex(T* t) { return hash_table::GetMutex(t); }
  125. private:
  126. typedef LRUList<T> LRUListType;
  127. typename hash_table::Bucket& GetBucket(const uint64_t h) {
  128. const uint32_t bucket_idx = h % hash_table::nbuckets_;
  129. return hash_table::buckets_[bucket_idx];
  130. }
  131. LRUListType& GetLRUList(const uint64_t h) {
  132. const uint32_t bucket_idx = h % hash_table::nbuckets_;
  133. const uint32_t lock_idx = bucket_idx % hash_table::nlocks_;
  134. return lru_lists_[lock_idx];
  135. }
  136. port::RWMutex& GetMutex(const uint64_t h) {
  137. const uint32_t bucket_idx = h % hash_table::nbuckets_;
  138. const uint32_t lock_idx = bucket_idx % hash_table::nlocks_;
  139. return hash_table::locks_[lock_idx];
  140. }
  141. std::unique_ptr<LRUListType[]> lru_lists_;
  142. };
  143. } // namespace ROCKSDB_NAMESPACE
  144. #endif