#pragma once
#include <string>
#include "cache/sharded_cache.h"
#include "port/port.h"
#include "util/autovector.h"
namespace rocksdb {
struct LRUHandle {
void* value;
void (*deleter)(const Slice&, void* value);
LRUHandle* next_hash;
LRUHandle* next;
LRUHandle* prev;
size_t charge; size_t key_length;
uint32_t refs;
char flags;
uint32_t hash;
char key_data[1];
Slice key() const {
if (next == this) {
return *(reinterpret_cast<Slice*>(value));
} else {
return Slice(key_data, key_length);
}
}
bool InCache() { return flags & 1; }
bool IsHighPri() { return flags & 2; }
bool InHighPriPool() { return flags & 4; }
void SetInCache(bool in_cache) {
if (in_cache) {
flags |= 1;
} else {
flags &= ~1;
}
}
void SetPriority(Cache::Priority priority) {
if (priority == Cache::Priority::HIGH) {
flags |= 2;
} else {
flags &= ~2;
}
}
void SetInHighPriPool(bool in_high_pri_pool) {
if (in_high_pri_pool) {
flags |= 4;
} else {
flags &= ~4;
}
}
void Free() {
assert((refs == 1 && InCache()) || (refs == 0 && !InCache()));
if (deleter) {
(*deleter)(key(), value);
}
delete[] reinterpret_cast<char*>(this);
}
};
class LRUHandleTable {
public:
LRUHandleTable();
~LRUHandleTable();
LRUHandle* Lookup(const Slice& key, uint32_t hash);
LRUHandle* Insert(LRUHandle* h);
LRUHandle* Remove(const Slice& key, uint32_t hash);
template <typename T>
void ApplyToAllCacheEntries(T func) {
for (uint32_t i = 0; i < length_; i++) {
LRUHandle* h = list_[i];
while (h != nullptr) {
auto n = h->next_hash;
assert(h->InCache());
func(h);
h = n;
}
}
}
private:
LRUHandle** FindPointer(const Slice& key, uint32_t hash);
void Resize();
LRUHandle** list_;
uint32_t length_;
uint32_t elems_;
};
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
public:
LRUCacheShard();
virtual ~LRUCacheShard();
virtual void SetCapacity(size_t capacity) override;
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override;
void SetHighPriorityPoolRatio(double high_pri_pool_ratio);
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
size_t charge,
void (*deleter)(const Slice& key, void* value),
Cache::Handle** handle,
Cache::Priority priority) override;
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
virtual bool Ref(Cache::Handle* handle) override;
virtual bool Release(Cache::Handle* handle,
bool force_erase = false) override;
virtual void Erase(const Slice& key, uint32_t hash) override;
virtual size_t GetUsage() const override;
virtual size_t GetPinnedUsage() const override;
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
bool thread_safe) override;
virtual void EraseUnRefEntries() override;
virtual std::string GetPrintableOptions() const override;
void TEST_GetLRUList(LRUHandle** lru, LRUHandle** lru_low_pri);
size_t TEST_GetLRUSize();
double GetHighPriPoolRatio();
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void *);
void operator delete[](void*);
private:
void LRU_Remove(LRUHandle* e);
void LRU_Insert(LRUHandle* e);
void MaintainPoolSize();
bool Unref(LRUHandle* e);
void EvictFromLRU(size_t charge, autovector<LRUHandle*>* deleted);
size_t capacity_;
size_t high_pri_pool_usage_;
bool strict_capacity_limit_;
double high_pri_pool_ratio_;
double high_pri_pool_capacity_;
LRUHandle lru_;
LRUHandle* lru_low_pri_;
LRUHandleTable table_;
size_t usage_;
size_t lru_usage_;
mutable port::Mutex mutex_;
};
class LRUCache : public ShardedCache {
public:
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
double high_pri_pool_ratio);
virtual ~LRUCache();
virtual const char* Name() const override { return "LRUCache"; }
virtual CacheShard* GetShard(int shard) override;
virtual const CacheShard* GetShard(int shard) const override;
virtual void* Value(Handle* handle) override;
virtual size_t GetCharge(Handle* handle) const override;
virtual uint32_t GetHash(Handle* handle) const override;
virtual void DisownData() override;
size_t TEST_GetLRUSize();
double GetHighPriPoolRatio();
private:
LRUCacheShard* shards_;
int num_shards_ = 0;
};
}