#pragma once
#include <cassert>
#include "port/likely.h"
#include "rocksdb/cache.h"
#include "rocksdb/cleanable.h"
namespace ROCKSDB_NAMESPACE {
template <class T>
class CachableEntry {
public:
CachableEntry() = default;
CachableEntry(T* value, Cache* cache, Cache::Handle* cache_handle,
bool own_value)
: value_(value)
, cache_(cache)
, cache_handle_(cache_handle)
, own_value_(own_value)
{
assert(value_ != nullptr ||
(cache_ == nullptr && cache_handle_ == nullptr && !own_value_));
assert(!!cache_ == !!cache_handle_);
assert(!cache_handle_ || !own_value_);
}
CachableEntry(const CachableEntry&) = delete;
CachableEntry& operator=(const CachableEntry&) = delete;
CachableEntry(CachableEntry&& rhs)
: value_(rhs.value_)
, cache_(rhs.cache_)
, cache_handle_(rhs.cache_handle_)
, own_value_(rhs.own_value_)
{
assert(value_ != nullptr ||
(cache_ == nullptr && cache_handle_ == nullptr && !own_value_));
assert(!!cache_ == !!cache_handle_);
assert(!cache_handle_ || !own_value_);
rhs.ResetFields();
}
CachableEntry& operator=(CachableEntry&& rhs) {
if (UNLIKELY(this == &rhs)) {
return *this;
}
ReleaseResource();
value_ = rhs.value_;
cache_ = rhs.cache_;
cache_handle_ = rhs.cache_handle_;
own_value_ = rhs.own_value_;
assert(value_ != nullptr ||
(cache_ == nullptr && cache_handle_ == nullptr && !own_value_));
assert(!!cache_ == !!cache_handle_);
assert(!cache_handle_ || !own_value_);
rhs.ResetFields();
return *this;
}
~CachableEntry() {
ReleaseResource();
}
bool IsEmpty() const {
return value_ == nullptr && cache_ == nullptr && cache_handle_ == nullptr &&
!own_value_;
}
bool IsCached() const {
assert(!!cache_ == !!cache_handle_);
return cache_handle_ != nullptr;
}
T* GetValue() const { return value_; }
Cache* GetCache() const { return cache_; }
Cache::Handle* GetCacheHandle() const { return cache_handle_; }
bool GetOwnValue() const { return own_value_; }
void Reset() {
ReleaseResource();
ResetFields();
}
void TransferTo(Cleanable* cleanable) {
if (cleanable) {
if (cache_handle_ != nullptr) {
assert(cache_ != nullptr);
cleanable->RegisterCleanup(&ReleaseCacheHandle, cache_, cache_handle_);
} else if (own_value_) {
cleanable->RegisterCleanup(&DeleteValue, value_, nullptr);
}
}
ResetFields();
}
void SetOwnedValue(T* value) {
assert(value != nullptr);
if (UNLIKELY(value_ == value && own_value_)) {
assert(cache_ == nullptr && cache_handle_ == nullptr);
return;
}
Reset();
value_ = value;
own_value_ = true;
}
void SetUnownedValue(T* value) {
assert(value != nullptr);
if (UNLIKELY(value_ == value && cache_ == nullptr &&
cache_handle_ == nullptr && !own_value_)) {
return;
}
Reset();
value_ = value;
assert(!own_value_);
}
void SetCachedValue(T* value, Cache* cache, Cache::Handle* cache_handle) {
assert(value != nullptr);
assert(cache != nullptr);
assert(cache_handle != nullptr);
if (UNLIKELY(value_ == value && cache_ == cache &&
cache_handle_ == cache_handle && !own_value_)) {
return;
}
Reset();
value_ = value;
cache_ = cache;
cache_handle_ = cache_handle;
assert(!own_value_);
}
private:
void ReleaseResource() {
if (LIKELY(cache_handle_ != nullptr)) {
assert(cache_ != nullptr);
cache_->Release(cache_handle_);
} else if (own_value_) {
delete value_;
}
}
void ResetFields() {
value_ = nullptr;
cache_ = nullptr;
cache_handle_ = nullptr;
own_value_ = false;
}
static void ReleaseCacheHandle(void* arg1, void* arg2) {
Cache* const cache = static_cast<Cache*>(arg1);
assert(cache);
Cache::Handle* const cache_handle = static_cast<Cache::Handle*>(arg2);
assert(cache_handle);
cache->Release(cache_handle);
}
static void DeleteValue(void* arg1, void* ) {
delete static_cast<T*>(arg1);
}
private:
T* value_ = nullptr;
Cache* cache_ = nullptr;
Cache::Handle* cache_handle_ = nullptr;
bool own_value_ = false;
};
}