Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/leveldb/util/cache.cc
Line
Count
Source
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
900k
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
438
  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
438
    assert(next != this);
  Branch (58:5): [True: 438, False: 0]
59
60
438
    return Slice(key_data, key_length);
61
438
  }
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
2.59k
  HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }
72
14.4M
  ~HandleTable() { delete[] list_; }
73
74
442
  LRUHandle* Lookup(const Slice& key, uint32_t hash) {
75
442
    return *FindPointer(key, hash);
76
442
  }
77
78
16
  LRUHandle* Insert(LRUHandle* h) {
79
16
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
80
16
    LRUHandle* old = *ptr;
81
16
    h->next_hash = (old == nullptr ? nullptr : old->next_hash);
  Branch (81:21): [True: 16, False: 0]
82
16
    *ptr = h;
83
16
    if (old == nullptr) {
  Branch (83:9): [True: 16, False: 0]
84
16
      ++elems_;
85
16
      if (elems_ > length_) {
  Branch (85:11): [True: 0, False: 16]
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
16
    }
91
16
    return old;
92
16
  }
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) {
  Branch (97:9): [True: 0, False: 0]
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
458
  LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
115
458
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
116
458
    while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {
  Branch (116:12): [True: 406, False: 52]
  Branch (116:12): [True: 0, False: 458]
  Branch (116:32): [True: 0, False: 406]
  Branch (116:56): [True: 0, False: 406]
117
0
      ptr = &(*ptr)->next_hash;
118
0
    }
119
458
    return ptr;
120
458
  }
121
122
2.59k
  void Resize() {
123
2.59k
    uint32_t new_length = 4;
124
2.59k
    while (new_length < elems_) {
  Branch (124:12): [True: 0, False: 2.59k]
125
0
      new_length *= 2;
126
0
    }
127
2.59k
    LRUHandle** new_list = new LRUHandle*[new_length];
128
2.59k
    memset(new_list, 0, sizeof(new_list[0]) * new_length);
129
2.59k
    uint32_t count = 0;
130
2.59k
    for (uint32_t i = 0; i < length_; i++) {
  Branch (130:26): [True: 0, False: 2.59k]
131
0
      LRUHandle* h = list_[i];
132
0
      while (h != nullptr) {
  Branch (132:14): [True: 0, False: 0]
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
2.59k
    assert(elems_ == count);
  Branch (142:5): [True: 2.59k, False: 0]
143
2.59k
    delete[] list_;
144
2.59k
    list_ = new_list;
145
2.59k
    length_ = new_length;
146
2.59k
  }
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
2.59k
  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) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
175
  void Unref(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
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
2.59k
LRUCache::LRUCache() : capacity_(0), usage_(0) {
198
  // Make empty circular linked lists.
199
2.59k
  lru_.next = &lru_;
200
2.59k
  lru_.prev = &lru_;
201
2.59k
  in_use_.next = &in_use_;
202
2.59k
  in_use_.prev = &in_use_;
203
2.59k
}
204
205
14.4M
LRUCache::~LRUCache() {
206
14.4M
  assert(in_use_.next == &in_use_);  // Error if caller has an unreleased handle
  Branch (206:3): [True: 14.4M, False: 0]
207
14.4M
  for (LRUHandle* e = lru_.next; e != &lru_;) {
  Branch (207:34): [True: 16, False: 14.4M]
208
16
    LRUHandle* next = e->next;
209
16
    assert(e->in_cache);
  Branch (209:5): [True: 16, False: 0]
210
16
    e->in_cache = false;
211
16
    assert(e->refs == 1);  // Invariant of lru_ list.
  Branch (211:5): [True: 16, False: 0]
212
16
    Unref(e);
213
16
    e = next;
214
16
  }
215
14.4M
}
216
217
406
void LRUCache::Ref(LRUHandle* e) {
218
406
  if (e->refs == 1 && e->in_cache) {  // If on lru_ list, move to in_use_ list.
  Branch (218:7): [True: 406, False: 0]
  Branch (218:23): [True: 406, False: 0]
219
406
    LRU_Remove(e);
220
406
    LRU_Append(&in_use_, e);
221
406
  }
222
406
  e->refs++;
223
406
}
224
225
438
void LRUCache::Unref(LRUHandle* e) {
226
438
  assert(e->refs > 0);
  Branch (226:3): [True: 438, False: 0]
227
438
  e->refs--;
228
438
  if (e->refs == 0) {  // Deallocate.
  Branch (228:7): [True: 16, False: 422]
229
16
    assert(!e->in_cache);
  Branch (229:5): [True: 16, False: 0]
230
16
    (*e->deleter)(e->key(), e->value);
231
16
    free(e);
232
422
  } else if (e->in_cache && e->refs == 1) {
  Branch (232:14): [True: 422, False: 0]
  Branch (232:29): [True: 422, False: 0]
233
    // No longer in use; move to lru_ list.
234
422
    LRU_Remove(e);
235
422
    LRU_Append(&lru_, e);
236
422
  }
237
438
}
238
239
828
void LRUCache::LRU_Remove(LRUHandle* e) {
240
828
  e->next->prev = e->prev;
241
828
  e->prev->next = e->next;
242
828
}
243
244
844
void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {
245
  // Make "e" newest entry by inserting just before *list
246
844
  e->next = list;
247
844
  e->prev = list->prev;
248
844
  e->prev->next = e;
249
844
  e->next->prev = e;
250
844
}
251
252
442
Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
253
442
  MutexLock l(&mutex_);
254
442
  LRUHandle* e = table_.Lookup(key, hash);
255
442
  if (e != nullptr) {
  Branch (255:7): [True: 406, False: 36]
256
406
    Ref(e);
257
406
  }
258
442
  return reinterpret_cast<Cache::Handle*>(e);
259
442
}
260
261
422
void LRUCache::Release(Cache::Handle* handle) {
262
422
  MutexLock l(&mutex_);
263
422
  Unref(reinterpret_cast<LRUHandle*>(handle));
264
422
}
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
16
                                                void* value)) {
270
16
  MutexLock l(&mutex_);
271
272
16
  LRUHandle* e =
273
16
      reinterpret_cast<LRUHandle*>(malloc(sizeof(LRUHandle) - 1 + key.size()));
274
16
  e->value = value;
275
16
  e->deleter = deleter;
276
16
  e->charge = charge;
277
16
  e->key_length = key.size();
278
16
  e->hash = hash;
279
16
  e->in_cache = false;
280
16
  e->refs = 1;  // for the returned handle.
281
16
  memcpy(e->key_data, key.data(), key.size());
282
283
16
  if (capacity_ > 0) {
  Branch (283:7): [True: 16, False: 0]
284
16
    e->refs++;  // for the cache's reference.
285
16
    e->in_cache = true;
286
16
    LRU_Append(&in_use_, e);
287
16
    usage_ += charge;
288
16
    FinishErase(table_.Insert(e));
289
16
  } 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
16
  while (usage_ > capacity_ && lru_.next != &lru_) {
  Branch (293:10): [True: 0, False: 16]
  Branch (293:32): [True: 0, False: 0]
294
0
    LRUHandle* old = lru_.next;
295
0
    assert(old->refs == 1);
  Branch (295:5): [True: 0, False: 0]
296
0
    bool erased = FinishErase(table_.Remove(old->key(), old->hash));
297
0
    if (!erased) {  // to avoid unused variable when compiled NDEBUG
  Branch (297:9): [True: 0, False: 0]
298
0
      assert(erased);
  Branch (298:7): [True: 0, False: 0]
299
0
    }
300
0
  }
301
302
16
  return reinterpret_cast<Cache::Handle*>(e);
303
16
}
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
16
bool LRUCache::FinishErase(LRUHandle* e) {
308
16
  if (e != nullptr) {
  Branch (308:7): [True: 0, False: 16]
309
0
    assert(e->in_cache);
  Branch (309:5): [True: 0, False: 0]
310
0
    LRU_Remove(e);
311
0
    e->in_cache = false;
312
0
    usage_ -= e->charge;
313
0
    Unref(e);
314
0
  }
315
16
  return e != nullptr;
316
16
}
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_) {
  Branch (325:10): [True: 0, False: 0]
326
0
    LRUHandle* e = lru_.next;
327
0
    assert(e->refs == 1);
  Branch (327:5): [True: 0, False: 0]
328
0
    bool erased = FinishErase(table_.Remove(e->key(), e->hash));
329
0
    if (!erased) {  // to avoid unused variable when compiled NDEBUG
  Branch (329:9): [True: 0, False: 0]
330
0
      assert(erased);
  Branch (330:7): [True: 0, False: 0]
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
458
  static inline uint32_t HashSlice(const Slice& s) {
345
458
    return Hash(s.data(), s.size(), 0);
346
458
  }
347
348
880
  static uint32_t Shard(uint32_t hash) { return hash >> (32 - kNumShardBits); }
349
350
 public:
351
162
  explicit ShardedLRUCache(size_t capacity) : last_id_(0) {
352
162
    const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
353
2.75k
    for (int s = 0; s < kNumShards; s++) {
  Branch (353:21): [True: 2.59k, False: 162]
354
2.59k
      shard_[s].SetCapacity(per_shard);
355
2.59k
    }
356
162
  }
357
900k
  ~ShardedLRUCache() override {}
358
  Handle* Insert(const Slice& key, void* value, size_t charge,
359
16
                 void (*deleter)(const Slice& key, void* value)) override {
360
16
    const uint32_t hash = HashSlice(key);
361
16
    return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
362
16
  }
363
442
  Handle* Lookup(const Slice& key) override {
364
442
    const uint32_t hash = HashSlice(key);
365
442
    return shard_[Shard(hash)].Lookup(key, hash);
366
442
  }
367
422
  void Release(Handle* handle) override {
368
422
    LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
369
422
    shard_[Shard(h->hash)].Release(handle);
370
422
  }
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
422
  void* Value(Handle* handle) override {
376
422
    return reinterpret_cast<LRUHandle*>(handle)->value;
377
422
  }
378
16
  uint64_t NewId() override {
379
16
    MutexLock l(&id_mutex_);
380
16
    return ++(last_id_);
381
16
  }
382
0
  void Prune() override {
383
0
    for (int s = 0; s < kNumShards; s++) {
  Branch (383:21): [True: 0, False: 0]
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++) {
  Branch (389:21): [True: 0, False: 0]
390
0
      total += shard_[s].TotalCharge();
391
0
    }
392
0
    return total;
393
0
  }
394
};
395
396
}  // end anonymous namespace
397
398
162
Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); }
399
400
}  // namespace leveldb