1 // Copyright (c) 2011-present, 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 // Copyright (c) 2012 The LevelDB Authors. All rights reserved. 7 // Use of this source code is governed by a BSD-style license that can be 8 // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 10 #pragma once 11 12 #include <cassert> 13 #include "port/likely.h" 14 #include "rocksdb/cache.h" 15 #include "rocksdb/cleanable.h" 16 17 namespace ROCKSDB_NAMESPACE { 18 19 // CachableEntry is a handle to an object that may or may not be in the block 20 // cache. It is used in a variety of ways: 21 // 22 // 1) It may refer to an object in the block cache. In this case, cache_ and 23 // cache_handle_ are not nullptr, and the cache handle has to be released when 24 // the CachableEntry is destroyed (the lifecycle of the cached object, on the 25 // other hand, is managed by the cache itself). 26 // 2) It may uniquely own the (non-cached) object it refers to (examples include 27 // a block read directly from file, or uncompressed blocks when there is a 28 // compressed block cache but no uncompressed block cache). In such cases, the 29 // object has to be destroyed when the CachableEntry is destroyed. 30 // 3) It may point to an object (cached or not) without owning it. In this case, 31 // no action is needed when the CachableEntry is destroyed. 32 // 4) Sometimes, management of a cached or owned object (see #1 and #2 above) 33 // is transferred to some other object. This is used for instance with iterators 34 // (where cleanup is performed using a chain of cleanup functions, 35 // see Cleanable). 36 // 37 // Because of #1 and #2 above, copying a CachableEntry is not safe (and thus not 38 // allowed); hence, this is a move-only type, where a move transfers the 39 // management responsibilities, and leaves the source object in an empty state. 40 41 template <class T> 42 class CachableEntry { 43 public: 44 CachableEntry() = default; 45 CachableEntry(T * value,Cache * cache,Cache::Handle * cache_handle,bool own_value)46 CachableEntry(T* value, Cache* cache, Cache::Handle* cache_handle, 47 bool own_value) 48 : value_(value) 49 , cache_(cache) 50 , cache_handle_(cache_handle) 51 , own_value_(own_value) 52 { 53 assert(value_ != nullptr || 54 (cache_ == nullptr && cache_handle_ == nullptr && !own_value_)); 55 assert(!!cache_ == !!cache_handle_); 56 assert(!cache_handle_ || !own_value_); 57 } 58 59 CachableEntry(const CachableEntry&) = delete; 60 CachableEntry& operator=(const CachableEntry&) = delete; 61 CachableEntry(CachableEntry && rhs)62 CachableEntry(CachableEntry&& rhs) 63 : value_(rhs.value_) 64 , cache_(rhs.cache_) 65 , cache_handle_(rhs.cache_handle_) 66 , own_value_(rhs.own_value_) 67 { 68 assert(value_ != nullptr || 69 (cache_ == nullptr && cache_handle_ == nullptr && !own_value_)); 70 assert(!!cache_ == !!cache_handle_); 71 assert(!cache_handle_ || !own_value_); 72 73 rhs.ResetFields(); 74 } 75 76 CachableEntry& operator=(CachableEntry&& rhs) { 77 if (UNLIKELY(this == &rhs)) { 78 return *this; 79 } 80 81 ReleaseResource(); 82 83 value_ = rhs.value_; 84 cache_ = rhs.cache_; 85 cache_handle_ = rhs.cache_handle_; 86 own_value_ = rhs.own_value_; 87 88 assert(value_ != nullptr || 89 (cache_ == nullptr && cache_handle_ == nullptr && !own_value_)); 90 assert(!!cache_ == !!cache_handle_); 91 assert(!cache_handle_ || !own_value_); 92 93 rhs.ResetFields(); 94 95 return *this; 96 } 97 ~CachableEntry()98 ~CachableEntry() { 99 ReleaseResource(); 100 } 101 IsEmpty()102 bool IsEmpty() const { 103 return value_ == nullptr && cache_ == nullptr && cache_handle_ == nullptr && 104 !own_value_; 105 } 106 IsCached()107 bool IsCached() const { 108 assert(!!cache_ == !!cache_handle_); 109 110 return cache_handle_ != nullptr; 111 } 112 GetValue()113 T* GetValue() const { return value_; } GetCache()114 Cache* GetCache() const { return cache_; } GetCacheHandle()115 Cache::Handle* GetCacheHandle() const { return cache_handle_; } GetOwnValue()116 bool GetOwnValue() const { return own_value_; } 117 Reset()118 void Reset() { 119 ReleaseResource(); 120 ResetFields(); 121 } 122 TransferTo(Cleanable * cleanable)123 void TransferTo(Cleanable* cleanable) { 124 if (cleanable) { 125 if (cache_handle_ != nullptr) { 126 assert(cache_ != nullptr); 127 cleanable->RegisterCleanup(&ReleaseCacheHandle, cache_, cache_handle_); 128 } else if (own_value_) { 129 cleanable->RegisterCleanup(&DeleteValue, value_, nullptr); 130 } 131 } 132 133 ResetFields(); 134 } 135 SetOwnedValue(T * value)136 void SetOwnedValue(T* value) { 137 assert(value != nullptr); 138 139 if (UNLIKELY(value_ == value && own_value_)) { 140 assert(cache_ == nullptr && cache_handle_ == nullptr); 141 return; 142 } 143 144 Reset(); 145 146 value_ = value; 147 own_value_ = true; 148 } 149 SetUnownedValue(T * value)150 void SetUnownedValue(T* value) { 151 assert(value != nullptr); 152 153 if (UNLIKELY(value_ == value && cache_ == nullptr && 154 cache_handle_ == nullptr && !own_value_)) { 155 return; 156 } 157 158 Reset(); 159 160 value_ = value; 161 assert(!own_value_); 162 } 163 SetCachedValue(T * value,Cache * cache,Cache::Handle * cache_handle)164 void SetCachedValue(T* value, Cache* cache, Cache::Handle* cache_handle) { 165 assert(value != nullptr); 166 assert(cache != nullptr); 167 assert(cache_handle != nullptr); 168 169 if (UNLIKELY(value_ == value && cache_ == cache && 170 cache_handle_ == cache_handle && !own_value_)) { 171 return; 172 } 173 174 Reset(); 175 176 value_ = value; 177 cache_ = cache; 178 cache_handle_ = cache_handle; 179 assert(!own_value_); 180 } 181 182 private: ReleaseResource()183 void ReleaseResource() { 184 if (LIKELY(cache_handle_ != nullptr)) { 185 assert(cache_ != nullptr); 186 cache_->Release(cache_handle_); 187 } else if (own_value_) { 188 delete value_; 189 } 190 } 191 ResetFields()192 void ResetFields() { 193 value_ = nullptr; 194 cache_ = nullptr; 195 cache_handle_ = nullptr; 196 own_value_ = false; 197 } 198 ReleaseCacheHandle(void * arg1,void * arg2)199 static void ReleaseCacheHandle(void* arg1, void* arg2) { 200 Cache* const cache = static_cast<Cache*>(arg1); 201 assert(cache); 202 203 Cache::Handle* const cache_handle = static_cast<Cache::Handle*>(arg2); 204 assert(cache_handle); 205 206 cache->Release(cache_handle); 207 } 208 DeleteValue(void * arg1,void *)209 static void DeleteValue(void* arg1, void* /* arg2 */) { 210 delete static_cast<T*>(arg1); 211 } 212 213 private: 214 T* value_ = nullptr; 215 Cache* cache_ = nullptr; 216 Cache::Handle* cache_handle_ = nullptr; 217 bool own_value_ = false; 218 }; 219 220 } // namespace ROCKSDB_NAMESPACE 221