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