test: Check btcsignals determinism in thread_safety test case
What changed, and why it matters
This is a test-only change for Bitcoin Core. It makes an existing unit test stricter by checking that one part of a multi-threaded signal callback counter is deterministic, and by testing connection/disconnection races more frequently. It does not change any production code, runtime behavior, or network protocol.
No security action required. Treat as normal quality/test improvement.
Security signals we found
No production code modified
Test-only change
No input parsing, cryptography, consensus, or networking changes
No memory-unsafe constructs introduced
No privilege or authorization changes
Evidence from the diff
The commit modifies src/test/btcsignals_tests.cpp only. It splits a single std::atomic
Changed components
src/test/btcsignals_tests.cppInspect captured patch +22 / −14
diff --git a/src/test/btcsignals_tests.cpp b/src/test/btcsignals_tests.cpp
index 7d7970bd..dd7fcb9b 100644
--- a/src/test/btcsignals_tests.cpp
+++ b/src/test/btcsignals_tests.cpp
@@ -111,17 +111,18 @@ BOOST_AUTO_TEST_CASE(any_of_combiner)
* Connect sig0 to an incrementor function and loop in a thread.
* Meanwhile, in another thread, inject and call new increment callbacks.
* Both threads are constantly calling empty/connected.
- * Though the end-result is undefined due to a non-deterministic number of
- * total callbacks executed, this should all be completely threadsafe.
+ * The end-result must be deterministic for the atomic modified by conn0.
+ * Though, the end-result for the atomic modified by the extra connections is
+ * undefined due to a non-deterministic number of total callbacks executed.
+ * In any case, this should all be completely threadsafe.
* Sanitizers should pick up any buggy data race behavior (if present).
*/
BOOST_AUTO_TEST_CASE(thread_safety)
{
btcsignals::signal<void()> sig0;
- std::atomic<uint32_t> val{0};
- auto conn0 = sig0.connect([&val] {
- val++;
- });
+ std::atomic<uint32_t> val_det{0};
+ std::atomic<uint32_t> val_non_det{0};
+ auto conn0 = sig0.connect([&val_det] { val_det++; });
std::thread incrementor([&conn0, &sig0] {
for (int i = 0; i < 1000; i++) {
@@ -134,16 +135,17 @@ BOOST_AUTO_TEST_CASE(thread_safety)
assert(conn0.connected());
});
- std::thread extra_increment_injector([&conn0, &sig0, &val] {
+ std::thread extra_increment_injector([&conn0, &sig0, &val_non_det] {
static constexpr size_t num_extra_conns{1000};
std::vector<btcsignals::scoped_connection> extra_conns;
extra_conns.reserve(num_extra_conns);
for (size_t i = 0; i < num_extra_conns; i++) {
BOOST_CHECK(!sig0.empty());
BOOST_CHECK(conn0.connected());
- extra_conns.emplace_back(sig0.connect([&val] {
- val++;
- }));
+ btcsignals::scoped_connection extra{sig0.connect([&val_non_det] { val_non_det++; })};
+ if (i % 2 == 0) {
+ extra_conns.emplace_back(std::move(extra));
+ }
sig0();
}
});
@@ -152,10 +154,16 @@ BOOST_AUTO_TEST_CASE(thread_safety)
conn0.disconnect();
BOOST_CHECK(sig0.empty());
- // sig will have been called 2000 times, and at least 1000 of those will
- // have been executing multiple incrementing callbacks. So while val is
- // probably MUCH bigger, it's guaranteed to be at least 3000.
- BOOST_CHECK_GE(val.load(), 3000);
+ // sig0 will have been called 2000 times, and only the first connection did
+ // increment val_det, so it must be 2000.
+ BOOST_CHECK_EQUAL(val_det.load(), 2000);
+ // The number of connections that increment val_non_det is growing from 1
+ // to 500, where 500 are disconnected immediately again after the step.
+ // Before the end of each step the connections are called at least once.
+ // However, it is unknown how often the connections have been called
+ // exactly. The 500th Triangular Number gives a lower estimate.
+ // T_n=n(n+1)/2
+ BOOST_CHECK_GE(val_non_det.load(), 500 * 501 / 2);
}
/* Test that connection and disconnection works from within signal
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.