Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
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 be3// found in the LICENSE file. See the AUTHORS file for names of contributors.5#include <assert.h>6#include <stdio.h>7#include <stdlib.h>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"15namespace leveldb {17Cache::~Cache() {}19namespace {21// LRU cache implementation22//23// Cache entries have an "in_cache" boolean indicating whether the cache has a24// reference on the entry. The only ways that this can become false without the25// entry being passed to its "deleter" are via Erase(), via Insert() when26// 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 the29// cache are in one list or the other, and never both. Items still referenced30// 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 no32// particular order. (This list is used for invariant checking. If we33// removed the check, elements that would otherwise be on this list could be34// left as disconnected singleton lists.)35// - LRU: contains the items not currently referenced by clients, in LRU order36// 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 only38// external reference.40// An entry is a variable length heap-allocated structure. Entries41// are kept in a circular doubly linked list ordered by access time.42struct LRUHandle {43void* value;44void (*deleter)(const Slice&, void* value);45LRUHandle* next_hash;46LRUHandle* next;47LRUHandle* prev;48size_t charge; // TODO(opt): Only allow uint32_t?49size_t key_length;50bool in_cache; // Whether entry is in the cache.51uint32_t refs; // References, including cache reference, if present.52uint32_t hash; // Hash of key(); used for fast sharding and comparisons53char key_data[1]; // Beginning of key55Slice key() const {56// next_ is only equal to this if the LRU handle is the list head of an57// empty list. List heads never have meaningful keys.58assert(next != this);60return Slice(key_data, key_length);61}62};64// We provide our own simple hash table since it removes a whole bunch65// of porting hacks and is also faster than some of the built-in hash66// table implementations in some of the compiler/runtime combinations67// we have tested. E.g., readrandom speeds up by ~5% over the g++68// 4.4.3's builtin hashtable.69class HandleTable {70public:71HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }72~HandleTable() { delete[] list_; }74LRUHandle* Lookup(const Slice& key, uint32_t hash) {75return *FindPointer(key, hash);76}78LRUHandle* Insert(LRUHandle* h) {79LRUHandle** ptr = FindPointer(h->key(), h->hash);80LRUHandle* old = *ptr;81h->next_hash = (old == nullptr ? nullptr : old->next_hash);82*ptr = h;83if (old == nullptr) {84++elems_;85if (elems_ > length_) {86// Since each cache entry is fairly large, we aim for a small87// average linked list length (<= 1).88Resize();89}90}91return old;92}94LRUHandle* Remove(const Slice& key, uint32_t hash) {95LRUHandle** ptr = FindPointer(key, hash);96LRUHandle* result = *ptr;97if (result != nullptr) {98*ptr = result->next_hash;99--elems_;100}101return result;102}104private:105// The table consists of an array of buckets where each bucket is106// a linked list of cache entries that hash into the bucket.107uint32_t length_;108uint32_t elems_;109LRUHandle** list_;111// Return a pointer to slot that points to a cache entry that112// matches key/hash. If there is no such cache entry, return a113// pointer to the trailing slot in the corresponding linked list.114LRUHandle** FindPointer(const Slice& key, uint32_t hash) {115LRUHandle** ptr = &list_[hash & (length_ - 1)];116while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) {117ptr = &(*ptr)->next_hash;118}119return ptr;120}122void Resize() {123uint32_t new_length = 4;124while (new_length < elems_) {125new_length *= 2;126}127LRUHandle** new_list = new LRUHandle*[new_length];128memset(new_list, 0, sizeof(new_list[0]) * new_length);129uint32_t count = 0;130for (uint32_t i = 0; i < length_; i++) {131LRUHandle* h = list_[i];132while (h != nullptr) {133LRUHandle* next = h->next_hash;134uint32_t hash = h->hash;135LRUHandle** ptr = &new_list[hash & (new_length - 1)];136h->next_hash = *ptr;137*ptr = h;138h = next;139count++;140}141}142assert(elems_ == count);143delete[] list_;144list_ = new_list;145length_ = new_length;146}147};149// A single shard of sharded cache.150class LRUCache {151public:152LRUCache();153~LRUCache();155// Separate from constructor so caller can easily make an array of LRUCache156void SetCapacity(size_t capacity) { capacity_ = capacity; }158// Like Cache methods, but with an extra "hash" parameter.159Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value,160size_t charge,161void (*deleter)(const Slice& key, void* value));162Cache::Handle* Lookup(const Slice& key, uint32_t hash);163void Release(Cache::Handle* handle);164void Erase(const Slice& key, uint32_t hash);165void Prune();166size_t TotalCharge() const {167MutexLock l(&mutex_);168return usage_;169}171private:172void LRU_Remove(LRUHandle* e);173void LRU_Append(LRUHandle* list, LRUHandle* e);174void Ref(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);175void Unref(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);176bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);178// Initialized before use.179size_t capacity_;181// mutex_ protects the following state.182mutable port::Mutex mutex_;183size_t usage_ GUARDED_BY(mutex_);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.188LRUHandle lru_ GUARDED_BY(mutex_);190// Dummy head of in-use list.191// Entries are in use by clients, and have refs >= 2 and in_cache==true.192LRUHandle in_use_ GUARDED_BY(mutex_);194HandleTable table_ GUARDED_BY(mutex_);195};197LRUCache::LRUCache() : capacity_(0), usage_(0) {198// Make empty circular linked lists.199lru_.next = &lru_;200lru_.prev = &lru_;201in_use_.next = &in_use_;202in_use_.prev = &in_use_;203}205LRUCache::~LRUCache() {206assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle207for (LRUHandle* e = lru_.next; e != &lru_;) {208LRUHandle* next = e->next;209assert(e->in_cache);210e->in_cache = false;211assert(e->refs == 1); // Invariant of lru_ list.212Unref(e);213e = next;214}215}217void LRUCache::Ref(LRUHandle* e) {218if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list.219LRU_Remove(e);220LRU_Append(&in_use_, e);221}222e->refs++;223}225void LRUCache::Unref(LRUHandle* e) {226assert(e->refs > 0);227e->refs--;228if (e->refs == 0) { // Deallocate.229assert(!e->in_cache);230(*e->deleter)(e->key(), e->value);231free(e);232} else if (e->in_cache && e->refs == 1) {233// No longer in use; move to lru_ list.234LRU_Remove(e);235LRU_Append(&lru_, e);236}237}239void LRUCache::LRU_Remove(LRUHandle* e) {240e->next->prev = e->prev;241e->prev->next = e->next;242}244void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {245// Make "e" newest entry by inserting just before *list246e->next = list;247e->prev = list->prev;248e->prev->next = e;249e->next->prev = e;250}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.