| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 | //  Copyright (c) 2013, Facebook, Inc.  All rights reserved.//  This source code is licensed under both the GPLv2 (found in the//  COPYING file in the root directory) and Apache 2.0 License//  (found in the LICENSE.Apache file in the root directory).//#pragma once#ifndef ROCKSDB_LITE#include <functional>#include "util/random.h"#include "utilities/persistent_cache/hash_table.h"#include "utilities/persistent_cache/lrulist.h"namespace ROCKSDB_NAMESPACE {// Evictable Hash Table//// Hash table index where least accessed (or one of the least accessed) elements// can be evicted.//// Please note EvictableHashTable can only be created for pointer type objectstemplate <class T, class Hash, class Equal>class EvictableHashTable : private HashTable<T*, Hash, Equal> { public:  typedef HashTable<T*, Hash, Equal> hash_table;  explicit EvictableHashTable(const size_t capacity = 1024 * 1024,                              const float load_factor = 2.0,                              const uint32_t nlocks = 256)      : HashTable<T*, Hash, Equal>(capacity, load_factor, nlocks),        lru_lists_(new LRUList<T>[hash_table::nlocks_]) {    assert(lru_lists_);  }  virtual ~EvictableHashTable() { AssertEmptyLRU(); }  //  // Insert given record to hash table (and LRU list)  //  bool Insert(T* t) {    const uint64_t h = Hash()(t);    typename hash_table::Bucket& bucket = GetBucket(h);    LRUListType& lru = GetLRUList(h);    port::RWMutex& lock = GetMutex(h);    WriteLock _(&lock);    if (hash_table::Insert(&bucket, t)) {      lru.Push(t);      return true;    }    return false;  }  //  // Lookup hash table  //  // Please note that read lock should be held by the caller. This is because  // the caller owns the data, and should hold the read lock as long as he  // operates on the data.  bool Find(T* t, T** ret) {    const uint64_t h = Hash()(t);    typename hash_table::Bucket& bucket = GetBucket(h);    LRUListType& lru = GetLRUList(h);    port::RWMutex& lock = GetMutex(h);    ReadLock _(&lock);    if (hash_table::Find(&bucket, t, ret)) {      ++(*ret)->refs_;      lru.Touch(*ret);      return true;    }    return false;  }  //  // Evict one of the least recently used object  //  T* Evict(const std::function<void(T*)>& fn = nullptr) {    uint32_t random = Random::GetTLSInstance()->Next();    const size_t start_idx = random % hash_table::nlocks_;    T* t = nullptr;    // iterate from start_idx .. 0 .. start_idx    for (size_t i = 0; !t && i < hash_table::nlocks_; ++i) {      const size_t idx = (start_idx + i) % hash_table::nlocks_;      WriteLock _(&hash_table::locks_[idx]);      LRUListType& lru = lru_lists_[idx];      if (!lru.IsEmpty() && (t = lru.Pop()) != nullptr) {        assert(!t->refs_);        // We got an item to evict, erase from the bucket        const uint64_t h = Hash()(t);        typename hash_table::Bucket& bucket = GetBucket(h);        T* tmp = nullptr;        bool status = hash_table::Erase(&bucket, t, &tmp);        assert(t == tmp);        (void)status;        assert(status);        if (fn) {          fn(t);        }        break;      }      assert(!t);    }    return t;  }  void Clear(void (*fn)(T*)) {    for (uint32_t i = 0; i < hash_table::nbuckets_; ++i) {      const uint32_t lock_idx = i % hash_table::nlocks_;      WriteLock _(&hash_table::locks_[lock_idx]);      auto& lru_list = lru_lists_[lock_idx];      auto& bucket = hash_table::buckets_[i];      for (auto* t : bucket.list_) {        lru_list.Unlink(t);        (*fn)(t);      }      bucket.list_.clear();    }    // make sure that all LRU lists are emptied    AssertEmptyLRU();  }  void AssertEmptyLRU() {#ifndef NDEBUG    for (uint32_t i = 0; i < hash_table::nlocks_; ++i) {      WriteLock _(&hash_table::locks_[i]);      auto& lru_list = lru_lists_[i];      assert(lru_list.IsEmpty());    }#endif  }  //  // Fetch the mutex associated with a key  // This call is used to hold the lock for a given data for extended period of  // time.  port::RWMutex* GetMutex(T* t) { return hash_table::GetMutex(t); } private:  typedef LRUList<T> LRUListType;  typename hash_table::Bucket& GetBucket(const uint64_t h) {    const uint32_t bucket_idx = h % hash_table::nbuckets_;    return hash_table::buckets_[bucket_idx];  }  LRUListType& GetLRUList(const uint64_t h) {    const uint32_t bucket_idx = h % hash_table::nbuckets_;    const uint32_t lock_idx = bucket_idx % hash_table::nlocks_;    return lru_lists_[lock_idx];  }  port::RWMutex& GetMutex(const uint64_t h) {    const uint32_t bucket_idx = h % hash_table::nbuckets_;    const uint32_t lock_idx = bucket_idx % hash_table::nlocks_;    return hash_table::locks_[lock_idx];  }  std::unique_ptr<LRUListType[]> lru_lists_;};}  // namespace ROCKSDB_NAMESPACE#endif
 |