Coverage Report

Created: 2025-02-21 14:37

/root/bitcoin/src/leveldb/util/cache.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5
#include <assert.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
9
#include "leveldb/cache.h"
10
#include "port/port.h"
11
#include "port/thread_annotations.h"
12
#include "util/hash.h"
13
#include "util/mutexlock.h"
14
15
namespace leveldb {
16
17
0
Cache::~Cache() {}
18
19
namespace {
20
21
// LRU cache implementation
22
//
23
// Cache entries have an "in_cache" boolean indicating whether the cache has a
24
// reference on the entry.  The only ways that this can become false without the
25
// entry being passed to its "deleter" are via Erase(), via Insert() when
26
// an element with a duplicate key is inserted, or on destruction of the cache.
27
//
28
// The cache keeps two linked lists of items in the cache.  All items in the
29
// cache are in one list or the other, and never both.  Items still referenced
30
// by clients but erased from the cache are in neither list.  The lists are:
31
// - in-use:  contains the items currently referenced by clients, in no
32
//   particular order.  (This list is used for invariant checking.  If we
33
//   removed the check, elements that would otherwise be on this list could be
34
//   left as disconnected singleton lists.)
35
// - LRU:  contains the items not currently referenced by clients, in LRU order
36
// Elements are moved between these lists by the Ref() and Unref() methods,
37
// when they detect an element in the cache acquiring or losing its only
38
// external reference.
39
40
// An entry is a variable length heap-allocated structure.  Entries
41
// are kept in a circular doubly linked list ordered by access time.
42
struct LRUHandle {
43
  void* value;
44
  void (*deleter)(const Slice&, void* value);
45
  LRUHandle* next_hash;
46
  LRUHandle* next;
47
  LRUHandle* prev;
48
  size_t charge;  // TODO(opt): Only allow uint32_t?
49
  size_t key_length;
50
  bool in_cache;     // Whether entry is in the cache.
51
  uint32_t refs;     // References, including cache reference, if present.
52
  uint32_t hash;     // Hash of key(); used for fast sharding and comparisons
53
  char key_data[1];  // Beginning of key
54
55
0
  Slice key() const {
56
    // next_ is only equal to this if the LRU handle is the list head of an
57
    // empty list. List heads never have meaningful keys.
58
0
    assert(next != this);
59
60
0
    return Slice(key_data, key_length);
61
0
  }
62
};
63
64
// We provide our own simple hash table since it removes a whole bunch
65
// of porting hacks and is also faster than some of the built-in hash
66
// table implementations in some of the compiler/runtime combinations
67
// we have tested.  E.g., readrandom speeds up by ~5% over the g++
68
// 4.4.3's builtin hashtable.
69
class HandleTable {
70
 public:
71
0
  HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }
72
0
  ~HandleTable() { delete[] list_; }
73
74
0
  LRUHandle* Lookup(const Slice& key, uint32_t hash) {
75
0
    return *FindPointer(key, hash);
76
0
  }
77
78
0
  LRUHandle* Insert(LRUHandle* h) {
79
0
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
80
0
    LRUHandle* old = *ptr;
81
0
    h->next_hash = (old == nullptr ? nullptr : old->next_hash);
82
0
    *ptr = h;
83
0
    if (old == nullptr) {
84
0
      ++elems_;
85
0
      if (elems_ > length_) {
86
        // Since each cache entry is fairly large, we aim for a small
87
        // average linked list length (<= 1).
88
0
        Resize();
89
0
      }
90
0
    }
91
0
    return old;
92
0
  }
93
94
0
  LRUHandle* Remove(const Slice& key, uint32_t hash) {
95
0
    LRUHandle** ptr = FindPointer(key, hash);
96
0
    LRUHandle* result = *ptr;
97
0
    if (result != nullptr) {
98
0
      *ptr = result->next_hash;
99
0
      --elems_;
100
0
    }
101
0
    return result;
102
0
  }
103
104
 private:
105
  // The table consists of an array of buckets where each bucket is
106
  // a linked list of cache entries that hash into the bucket.
107
  uint32_t length_;
108
  uint32_t elems_;
109
  LRUHandle** list_;
110
111
  // Return a pointer to slot that points to a cache entry that
112
  // matches key/hash.  If there is no such cache entry, return a
113
  // pointer to the trailing slot in the corresponding linked list.
114
0
  LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
115
0
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
116
0
    while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {
117
0
      ptr = &(*ptr)->next_hash;
118
0
    }
119
0
    return ptr;
120
0
  }
121
122
0
  void Resize() {
123
0
    uint32_t new_length = 4;
124
0
    while (new_length < elems_) {
125
0
      new_length *= 2;
126
0
    }
127
0
    LRUHandle** new_list = new LRUHandle*[new_length];
128
0
    memset(new_list, 0, sizeof(new_list[0]) * new_length);
129
0
    uint32_t count = 0;
130
0
    for (uint32_t i = 0; i < length_; i++) {
131
0
      LRUHandle* h = list_[i];
132
0
      while (h != nullptr) {
133
0
        LRUHandle* next = h->next_hash;
134
0
        uint32_t hash = h->hash;
135
0
        LRUHandle** ptr = &new_list[hash & (new_length - 1)];
136
0
        h->next_hash = *ptr;
137
0
        *ptr = h;
138
0
        h = next;
139
0
        count++;
140
0
      }
141
0
    }
142
0
    assert(elems_ == count);
143
0
    delete[] list_;
144
0
    list_ = new_list;
145
0
    length_ = new_length;
146
0
  }
147
};
148
149
// A single shard of sharded cache.
150
class LRUCache {
151
 public:
152
  LRUCache();
153
  ~LRUCache();
154
155
  // Separate from constructor so caller can easily make an array of LRUCache
156
0
  void SetCapacity(size_t capacity) { capacity_ = capacity; }
157
158
  // Like Cache methods, but with an extra "hash" parameter.
159
  Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value,
160
                        size_t charge,
161
                        void (*deleter)(const Slice& key, void* value));
162
  Cache::Handle* Lookup(const Slice& key, uint32_t hash);
163
  void Release(Cache::Handle* handle);
164
  void Erase(const Slice& key, uint32_t hash);
165
  void Prune();
166
0
  size_t TotalCharge() const {
167
0
    MutexLock l(&mutex_);
168
0
    return usage_;
169
0
  }
170
171
 private:
172
  void LRU_Remove(LRUHandle* e);
173
  void LRU_Append(LRUHandle* list, LRUHandle* e);
174
  void Ref(LRUHandle* e);
175
  void Unref(LRUHandle* e);
176
  bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
177
178
  // Initialized before use.
179
  size_t capacity_;
180
181
  // mutex_ protects the following state.
182
  mutable port::Mutex mutex_;
183
  size_t usage_ GUARDED_BY(mutex_);
184
185
  // Dummy head of LRU list.
186
  // lru.prev is newest entry, lru.next is oldest entry.
187
  // Entries have refs==1 and in_cache==true.
188
  LRUHandle lru_ GUARDED_BY(mutex_);
189
190
  // Dummy head of in-use list.
191
  // Entries are in use by clients, and have refs >= 2 and in_cache==true.
192
  LRUHandle in_use_ GUARDED_BY(mutex_);
193
194
  HandleTable table_ GUARDED_BY(mutex_);
195
};
196
197
0
LRUCache::LRUCache() : capacity_(0), usage_(0) {
198
  // Make empty circular linked lists.
199
0
  lru_.next = &lru_;
200
0
  lru_.prev = &lru_;
201
0
  in_use_.next = &in_use_;
202
0
  in_use_.prev = &in_use_;
203
0
}
204
205
0
LRUCache::~LRUCache() {
206
0
  assert(in_use_.next == &in_use_);  // Error if caller has an unreleased handle
207
0
  for (LRUHandle* e = lru_.next; e != &lru_;) {
208
0
    LRUHandle* next = e->next;
209
0
    assert(e->in_cache);
210
0
    e->in_cache = false;
211
0
    assert(e->refs == 1);  // Invariant of lru_ list.
212
0
    Unref(e);
213
0
    e = next;
214
0
  }
215
0
}
216
217
0
void LRUCache::Ref(LRUHandle* e) {
218
0
  if (e->refs == 1 && e->in_cache) {  // If on lru_ list, move to in_use_ list.
219
0
    LRU_Remove(e);
220
0
    LRU_Append(&in_use_, e);
221
0
  }
222
0
  e->refs++;
223
0
}
224
225
0
void LRUCache::Unref(LRUHandle* e) {
226
0
  assert(e->refs > 0);
227
0
  e->refs--;
228
0
  if (e->refs == 0) {  // Deallocate.
229
0
    assert(!e->in_cache);
230
0
    (*e->deleter)(e->key(), e->value);
231
0
    free(e);
232
0
  } else if (e->in_cache && e->refs == 1) {
233
    // No longer in use; move to lru_ list.
234
0
    LRU_Remove(e);
235
0
    LRU_Append(&lru_, e);
236
0
  }
237
0
}
238
239
0
void LRUCache::LRU_Remove(LRUHandle* e) {
240
0
  e->next->prev = e->prev;
241
0
  e->prev->next = e->next;
242
0
}
243
244
0
void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {
245
  // Make "e" newest entry by inserting just before *list
246
0
  e->next = list;
247
0
  e->prev = list->prev;
248
0
  e->prev->next = e;
249
0
  e->next->prev = e;
250
0
}
251
252
0
Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
253
0
  MutexLock l(&mutex_);
254
0
  LRUHandle* e = table_.Lookup(key, hash);
255
0
  if (e != nullptr) {
256
0
    Ref(e);
257
0
  }
258
0
  return reinterpret_cast<Cache::Handle*>(e);
259
0
}
260
261
0
void LRUCache::Release(Cache::Handle* handle) {
262
0
  MutexLock l(&mutex_);
263
0
  Unref(reinterpret_cast<LRUHandle*>(handle));
264
0
}
265
266
Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value,
267
                                size_t charge,
268
                                void (*deleter)(const Slice& key,
269
0
                                                void* value)) {
270
0
  MutexLock l(&mutex_);
271
272
0
  LRUHandle* e =
273
0
      reinterpret_cast<LRUHandle*>(malloc(sizeof(LRUHandle) - 1 + key.size()));
274
0
  e->value = value;
275
0
  e->deleter = deleter;
276
0
  e->charge = charge;
277
0
  e->key_length = key.size();
278
0
  e->hash = hash;
279
0
  e->in_cache = false;
280
0
  e->refs = 1;  // for the returned handle.
281
0
  memcpy(e->key_data, key.data(), key.size());
282
283
0
  if (capacity_ > 0) {
284
0
    e->refs++;  // for the cache's reference.
285
0
    e->in_cache = true;
286
0
    LRU_Append(&in_use_, e);
287
0
    usage_ += charge;
288
0
    FinishErase(table_.Insert(e));
289
0
  } else {  // don't cache. (capacity_==0 is supported and turns off caching.)
290
    // next is read by key() in an assert, so it must be initialized
291
0
    e->next = nullptr;
292
0
  }
293
0
  while (usage_ > capacity_ && lru_.next != &lru_) {
294
0
    LRUHandle* old = lru_.next;
295
0
    assert(old->refs == 1);
296
0
    bool erased = FinishErase(table_.Remove(old->key(), old->hash));
297
0
    if (!erased) {  // to avoid unused variable when compiled NDEBUG
298
0
      assert(erased);
299
0
    }
300
0
  }
301
302
0
  return reinterpret_cast<Cache::Handle*>(e);
303
0
}
304
305
// If e != nullptr, finish removing *e from the cache; it has already been
306
// removed from the hash table.  Return whether e != nullptr.
307
0
bool LRUCache::FinishErase(LRUHandle* e) {
308
0
  if (e != nullptr) {
309
0
    assert(e->in_cache);
310
0
    LRU_Remove(e);
311
0
    e->in_cache = false;
312
0
    usage_ -= e->charge;
313
0
    Unref(e);
314
0
  }
315
0
  return e != nullptr;
316
0
}
317
318
0
void LRUCache::Erase(const Slice& key, uint32_t hash) {
319
0
  MutexLock l(&mutex_);
320
0
  FinishErase(table_.Remove(key, hash));
321
0
}
322
323
0
void LRUCache::Prune() {
324
0
  MutexLock l(&mutex_);
325
0
  while (lru_.next != &lru_) {
326
0
    LRUHandle* e = lru_.next;
327
0
    assert(e->refs == 1);
328
0
    bool erased = FinishErase(table_.Remove(e->key(), e->hash));
329
0
    if (!erased) {  // to avoid unused variable when compiled NDEBUG
330
0
      assert(erased);
331
0
    }
332
0
  }
333
0
}
334
335
static const int kNumShardBits = 4;
336
static const int kNumShards = 1 << kNumShardBits;
337
338
class ShardedLRUCache : public Cache {
339
 private:
340
  LRUCache shard_[kNumShards];
341
  port::Mutex id_mutex_;
342
  uint64_t last_id_;
343
344
0
  static inline uint32_t HashSlice(const Slice& s) {
345
0
    return Hash(s.data(), s.size(), 0);
346
0
  }
347
348
0
  static uint32_t Shard(uint32_t hash) { return hash >> (32 - kNumShardBits); }
349
350
 public:
351
0
  explicit ShardedLRUCache(size_t capacity) : last_id_(0) {
352
0
    const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
353
0
    for (int s = 0; s < kNumShards; s++) {
354
0
      shard_[s].SetCapacity(per_shard);
355
0
    }
356
0
  }
357
0
  ~ShardedLRUCache() override {}
358
  Handle* Insert(const Slice& key, void* value, size_t charge,
359
0
                 void (*deleter)(const Slice& key, void* value)) override {
360
0
    const uint32_t hash = HashSlice(key);
361
0
    return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
362
0
  }
363
0
  Handle* Lookup(const Slice& key) override {
364
0
    const uint32_t hash = HashSlice(key);
365
0
    return shard_[Shard(hash)].Lookup(key, hash);
366
0
  }
367
0
  void Release(Handle* handle) override {
368
0
    LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
369
0
    shard_[Shard(h->hash)].Release(handle);
370
0
  }
371
0
  void Erase(const Slice& key) override {
372
0
    const uint32_t hash = HashSlice(key);
373
0
    shard_[Shard(hash)].Erase(key, hash);
374
0
  }
375
0
  void* Value(Handle* handle) override {
376
0
    return reinterpret_cast<LRUHandle*>(handle)->value;
377
0
  }
378
0
  uint64_t NewId() override {
379
0
    MutexLock l(&id_mutex_);
380
0
    return ++(last_id_);
381
0
  }
382
0
  void Prune() override {
383
0
    for (int s = 0; s < kNumShards; s++) {
384
0
      shard_[s].Prune();
385
0
    }
386
0
  }
387
0
  size_t TotalCharge() const override {
388
0
    size_t total = 0;
389
0
    for (int s = 0; s < kNumShards; s++) {
390
0
      total += shard_[s].TotalCharge();
391
0
    }
392
0
    return total;
393
0
  }
394
};
395
396
}  // end anonymous namespace
397
398
0
Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); }
399
400
}  // namespace leveldb