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) The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <test/util/setup_common.h>6#include <util/btcsignals.h>8#include <boost/test/unit_test.hpp>10#include <semaphore>12namespace {15void IncrementCallback(int& val)16{17val++;18}19void SquareCallback(int& val)20{21val *= val;22}24bool ReturnTrue()25{26return true;27}28bool ReturnFalse()29{30return false;31}33} // anonymous namespace35BOOST_FIXTURE_TEST_SUITE(btcsignals_tests, BasicTestingSetup)37/* Callbacks should always be executed in the order in which they were added38*/39BOOST_AUTO_TEST_CASE(callback_order)40{41btcsignals::signal<void(int&)> sig0;42sig0.connect(IncrementCallback);43sig0.connect(SquareCallback);44int val{3};45sig0(val);46BOOST_CHECK_EQUAL(val, 16);47BOOST_CHECK(!sig0.empty());48}50BOOST_AUTO_TEST_CASE(disconnects)51{52btcsignals::signal<void(int&)> sig0;53auto conn0 = sig0.connect(IncrementCallback);54auto conn1 = sig0.connect(SquareCallback);55conn1.disconnect();56BOOST_CHECK(!sig0.empty());57int val{3};58sig0(val);59BOOST_CHECK_EQUAL(val, 4);61BOOST_CHECK(!sig0.empty());62conn0.disconnect();63BOOST_CHECK(sig0.empty());64sig0(val);65BOOST_CHECK_EQUAL(val, 4);67conn0 = sig0.connect(IncrementCallback);68conn1 = sig0.connect(IncrementCallback);69BOOST_CHECK(!sig0.empty());70sig0(val);71BOOST_CHECK_EQUAL(val, 6);72conn1.disconnect();74BOOST_CHECK(conn0.connected());75{76btcsignals::scoped_connection scope(conn0);77}78BOOST_CHECK(!conn0.connected());79BOOST_CHECK(sig0.empty());80sig0(val);81BOOST_CHECK_EQUAL(val, 6);82}84BOOST_AUTO_TEST_CASE(any_of_combiner)85{86btcsignals::signal<bool(), btcsignals::any_of> sig0;87decltype(sig0)::result_type ret;88ret = sig0();89BOOST_CHECK_EQUAL(ret, false);90{91btcsignals::scoped_connection conn0{sig0.connect(ReturnTrue)};92ret = sig0();93BOOST_CHECK_EQUAL(ret, true);94}95ret = sig0();96BOOST_CHECK_EQUAL(ret, false);97{98btcsignals::scoped_connection conn0{sig0.connect(ReturnTrue)};99btcsignals::scoped_connection conn1{sig0.connect(ReturnFalse)};100ret = sig0();101BOOST_CHECK_EQUAL(ret, true);102conn0.disconnect();103ret = sig0();104BOOST_CHECK_EQUAL(ret, false);105}106ret = sig0();107BOOST_CHECK_EQUAL(ret, false);108}110/* Test the thread-safety of connect/disconnect/empty/connected/callbacks.111* Connect sig0 to an incrementor function and loop in a thread.112* Meanwhile, in another thread, inject and call new increment callbacks.113* Both threads are constantly calling empty/connected.114* The end-result must be deterministic for the atomic modified by conn0.115* Though, the end-result for the atomic modified by the extra connections is116* undefined due to a non-deterministic number of total callbacks executed.117* In any case, this should all be completely threadsafe.118* Sanitizers should pick up any buggy data race behavior (if present).119*/120BOOST_AUTO_TEST_CASE(thread_safety)121{122btcsignals::signal<void()> sig0;123std::atomic<uint32_t> val_det{0};124std::atomic<uint32_t> val_non_det{0};125auto conn0 = sig0.connect([&val_det] { val_det++; });127std::thread incrementor([&conn0, &sig0] {128for (int i = 0; i < 1000; i++) {129sig0();130}131// Because these calls are purposely happening on both threads at the132// same time, these must be asserts rather than BOOST_CHECKs to prevent133// a race inside of BOOST_CHECK itself (writing to the log).134assert(!sig0.empty());135assert(conn0.connected());136});138std::thread extra_increment_injector([&conn0, &sig0, &val_non_det] {139static constexpr size_t num_extra_conns{1000};140std::vector<btcsignals::scoped_connection> extra_conns;141extra_conns.reserve(num_extra_conns);142for (size_t i = 0; i < num_extra_conns; i++) {143BOOST_CHECK(!sig0.empty());144BOOST_CHECK(conn0.connected());145btcsignals::scoped_connection extra{sig0.connect([&val_non_det] { val_non_det++; })};146if (i % 2 == 0) {147extra_conns.emplace_back(std::move(extra));148}149sig0();150}151});152incrementor.join();153extra_increment_injector.join();154conn0.disconnect();155BOOST_CHECK(sig0.empty());157// sig0 will have been called 2000 times, and only the first connection did158// increment val_det, so it must be 2000.159BOOST_CHECK_EQUAL(val_det.load(), 2000);160// The number of connections that increment val_non_det is growing from 1161// to 500, where 500 are disconnected immediately again after the step.162// Before the end of each step the connections are called at least once.163// However, it is unknown how often the connections have been called164// exactly. The 500th Triangular Number gives a lower estimate.165// T_n=n(n+1)/2166BOOST_CHECK_GE(val_non_det.load(), 500 * 501 / 2);167}169/* Test that connection and disconnection works from within signal170* callbacks.171*/172BOOST_AUTO_TEST_CASE(recursion_safety)173{174btcsignals::connection conn0, conn1, conn2;175btcsignals::signal<void()> sig0;176bool nonrecursive_callback_ran{false};177bool recursive_callback_ran{false};179conn0 = sig0.connect([&] {180BOOST_CHECK(!sig0.empty());181nonrecursive_callback_ran = true;182});183BOOST_CHECK(!nonrecursive_callback_ran);184sig0();185BOOST_CHECK(nonrecursive_callback_ran);186BOOST_CHECK(conn0.connected());188nonrecursive_callback_ran = false;189conn1 = sig0.connect([&] {190nonrecursive_callback_ran = true;191conn1.disconnect();192});193BOOST_CHECK(!nonrecursive_callback_ran);194BOOST_CHECK(conn0.connected());195BOOST_CHECK(conn1.connected());196sig0();197BOOST_CHECK(nonrecursive_callback_ran);198BOOST_CHECK(conn0.connected());199BOOST_CHECK(!conn1.connected());201nonrecursive_callback_ran = false;202conn1 = sig0.connect([&] {203conn2 = sig0.connect([&] {204BOOST_CHECK(conn0.connected());205recursive_callback_ran = true;206conn0.disconnect();207conn2.disconnect();208});209nonrecursive_callback_ran = true;210conn1.disconnect();211});212BOOST_CHECK(!nonrecursive_callback_ran);213BOOST_CHECK(!recursive_callback_ran);214BOOST_CHECK(conn0.connected());215BOOST_CHECK(conn1.connected());216BOOST_CHECK(!conn2.connected());217sig0();218BOOST_CHECK(nonrecursive_callback_ran);219BOOST_CHECK(!recursive_callback_ran);220BOOST_CHECK(conn0.connected());221BOOST_CHECK(!conn1.connected());222BOOST_CHECK(conn2.connected());223sig0();224BOOST_CHECK(recursive_callback_ran);225BOOST_CHECK(!conn0.connected());226BOOST_CHECK(!conn1.connected());227BOOST_CHECK(!conn2.connected());228}230/* Test that disconnection from another thread works in real time231*/232BOOST_AUTO_TEST_CASE(disconnect_thread_safety)233{234btcsignals::connection conn0, conn1, conn2;235btcsignals::signal<void(int&)> sig0;236std::binary_semaphore done1{0};237std::binary_semaphore done2{0};238int val{0};240conn0 = sig0.connect([&](int&) {241conn1.disconnect();242done1.release();243done2.acquire();244});245conn1 = sig0.connect(IncrementCallback);246conn2 = sig0.connect(IncrementCallback);247std::thread thr([&] {248done1.acquire();249conn2.disconnect();250done2.release();
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.