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 "db/skiplist.h"7#include <atomic>8#include <set>10#include "leveldb/env.h"11#include "port/port.h"12#include "port/thread_annotations.h"13#include "util/arena.h"14#include "util/hash.h"15#include "util/random.h"16#include "util/testharness.h"18namespace leveldb {20typedef uint64_t Key;22struct Comparator {23int operator()(const Key& a, const Key& b) const {24if (a < b) {25return -1;26} else if (a > b) {27return +1;28} else {29return 0;30}31}32};34class SkipTest {};36TEST(SkipTest, Empty) {37Arena arena;38Comparator cmp;39SkipList<Key, Comparator> list(cmp, &arena);40ASSERT_TRUE(!list.Contains(10));42SkipList<Key, Comparator>::Iterator iter(&list);43ASSERT_TRUE(!iter.Valid());44iter.SeekToFirst();45ASSERT_TRUE(!iter.Valid());46iter.Seek(100);47ASSERT_TRUE(!iter.Valid());48iter.SeekToLast();49ASSERT_TRUE(!iter.Valid());50}52TEST(SkipTest, InsertAndLookup) {53const int N = 2000;54const int R = 5000;55Random rnd(1000);56std::set<Key> keys;57Arena arena;58Comparator cmp;59SkipList<Key, Comparator> list(cmp, &arena);60for (int i = 0; i < N; i++) {61Key key = rnd.Next() % R;62if (keys.insert(key).second) {63list.Insert(key);64}65}67for (int i = 0; i < R; i++) {68if (list.Contains(i)) {69ASSERT_EQ(keys.count(i), 1);70} else {71ASSERT_EQ(keys.count(i), 0);72}73}75// Simple iterator tests76{77SkipList<Key, Comparator>::Iterator iter(&list);78ASSERT_TRUE(!iter.Valid());80iter.Seek(0);81ASSERT_TRUE(iter.Valid());82ASSERT_EQ(*(keys.begin()), iter.key());84iter.SeekToFirst();85ASSERT_TRUE(iter.Valid());86ASSERT_EQ(*(keys.begin()), iter.key());88iter.SeekToLast();89ASSERT_TRUE(iter.Valid());90ASSERT_EQ(*(keys.rbegin()), iter.key());91}93// Forward iteration test94for (int i = 0; i < R; i++) {95SkipList<Key, Comparator>::Iterator iter(&list);96iter.Seek(i);98// Compare against model iterator99std::set<Key>::iterator model_iter = keys.lower_bound(i);100for (int j = 0; j < 3; j++) {101if (model_iter == keys.end()) {102ASSERT_TRUE(!iter.Valid());103break;104} else {105ASSERT_TRUE(iter.Valid());106ASSERT_EQ(*model_iter, iter.key());107++model_iter;108iter.Next();109}110}111}113// Backward iteration test114{115SkipList<Key, Comparator>::Iterator iter(&list);116iter.SeekToLast();118// Compare against model iterator119for (std::set<Key>::reverse_iterator model_iter = keys.rbegin();120model_iter != keys.rend(); ++model_iter) {121ASSERT_TRUE(iter.Valid());122ASSERT_EQ(*model_iter, iter.key());123iter.Prev();124}125ASSERT_TRUE(!iter.Valid());126}127}129// We want to make sure that with a single writer and multiple130// concurrent readers (with no synchronization other than when a131// reader's iterator is created), the reader always observes all the132// data that was present in the skip list when the iterator was133// constructed. Because insertions are happening concurrently, we may134// also observe new values that were inserted since the iterator was135// constructed, but we should never miss any values that were present136// at iterator construction time.137//138// We generate multi-part keys:139// <key,gen,hash>140// where:141// key is in range [0..K-1]142// gen is a generation number for key143// hash is hash(key,gen)144//145// The insertion code picks a random key, sets gen to be 1 + the last146// generation number inserted for that key, and sets hash to Hash(key,gen).147//148// At the beginning of a read, we snapshot the last inserted149// generation number for each key. We then iterate, including random150// calls to Next() and Seek(). For every key we encounter, we151// check that it is either expected given the initial snapshot or has152// been concurrently added since the iterator started.153class ConcurrentTest {154private:155static const uint32_t K = 4;157static uint64_t key(Key key) { return (key >> 40); }158static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; }159static uint64_t hash(Key key) { return key & 0xff; }161static uint64_t HashNumbers(uint64_t k, uint64_t g) {162uint64_t data[2] = {k, g};163return Hash(reinterpret_cast<char*>(data), sizeof(data), 0);164}166static Key MakeKey(uint64_t k, uint64_t g) {167static_assert(sizeof(Key) == sizeof(uint64_t), "");168assert(k <= K); // We sometimes pass K to seek to the end of the skiplist169assert(g <= 0xffffffffu);170return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff));171}173static bool IsValidKey(Key k) {174return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff);175}177static Key RandomTarget(Random* rnd) {178switch (rnd->Next() % 10) {179case 0:180// Seek to beginning181return MakeKey(0, 0);182case 1:183// Seek to end184return MakeKey(K, 0);185default:186// Seek to middle187return MakeKey(rnd->Next() % K, 0);188}189}191// Per-key generation192struct State {193std::atomic<int> generation[K];194void Set(int k, int v) {195generation[k].store(v, std::memory_order_release);196}197int Get(int k) { return generation[k].load(std::memory_order_acquire); }199State() {200for (int k = 0; k < K; k++) {201Set(k, 0);202}203}204};206// Current state of the test207State current_;209Arena arena_;211// SkipList is not protected by mu_. We just use a single writer212// thread to modify it.213SkipList<Key, Comparator> list_;215public:216ConcurrentTest() : list_(Comparator(), &arena_) {}218// REQUIRES: External synchronization219void WriteStep(Random* rnd) {220const uint32_t k = rnd->Next() % K;221const intptr_t g = current_.Get(k) + 1;222const Key key = MakeKey(k, g);223list_.Insert(key);224current_.Set(k, g);225}227void ReadStep(Random* rnd) {228// Remember the initial committed state of the skiplist.229State initial_state;230for (int k = 0; k < K; k++) {231initial_state.Set(k, current_.Get(k));232}234Key pos = RandomTarget(rnd);235SkipList<Key, Comparator>::Iterator iter(&list_);236iter.Seek(pos);237while (true) {238Key current;239if (!iter.Valid()) {240current = MakeKey(K, 0);241} else {242current = iter.key();243ASSERT_TRUE(IsValidKey(current)) << current;244}245ASSERT_LE(pos, current) << "should not go backwards";247// Verify that everything in [pos,current) was not present in248// initial_state.249while (pos < current) {250ASSERT_LT(key(pos), K) << pos;
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.