Bitcoin Core 31.99.0
P2P Digital Currency
btcsignals_tests.cpp
Go to the documentation of this file.
1// Copyright (c) The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6#include <util/btcsignals.h>
7
8#include <boost/test/unit_test.hpp>
9
10#include <semaphore>
11
12namespace {
13
14
15void IncrementCallback(int& val)
16{
17 val++;
18}
19void SquareCallback(int& val)
20{
21 val *= val;
22}
23
24bool ReturnTrue()
25{
26 return true;
27}
28bool ReturnFalse()
29{
30 return false;
31}
32
33} // anonymous namespace
34
36
37/* Callbacks should always be executed in the order in which they were added
38 */
39BOOST_AUTO_TEST_CASE(callback_order)
40{
41 btcsignals::signal<void(int&)> sig0;
42 sig0.connect(IncrementCallback);
43 sig0.connect(SquareCallback);
44 int val{3};
45 sig0(val);
46 BOOST_CHECK_EQUAL(val, 16);
47 BOOST_CHECK(!sig0.empty());
48}
49
51{
52 btcsignals::signal<void(int&)> sig0;
53 auto conn0 = sig0.connect(IncrementCallback);
54 auto conn1 = sig0.connect(SquareCallback);
55 conn1.disconnect();
56 BOOST_CHECK(!sig0.empty());
57 int val{3};
58 sig0(val);
59 BOOST_CHECK_EQUAL(val, 4);
60
61 BOOST_CHECK(!sig0.empty());
62 conn0.disconnect();
63 BOOST_CHECK(sig0.empty());
64 sig0(val);
65 BOOST_CHECK_EQUAL(val, 4);
66
67 conn0 = sig0.connect(IncrementCallback);
68 conn1 = sig0.connect(IncrementCallback);
69 BOOST_CHECK(!sig0.empty());
70 sig0(val);
71 BOOST_CHECK_EQUAL(val, 6);
72 conn1.disconnect();
73
74 BOOST_CHECK(conn0.connected());
75 {
77 }
78 BOOST_CHECK(!conn0.connected());
79 BOOST_CHECK(sig0.empty());
80 sig0(val);
81 BOOST_CHECK_EQUAL(val, 6);
82}
83
84BOOST_AUTO_TEST_CASE(any_of_combiner)
85{
87 decltype(sig0)::result_type ret;
88 ret = sig0();
89 BOOST_CHECK_EQUAL(ret, false);
90 {
91 btcsignals::scoped_connection conn0{sig0.connect(ReturnTrue)};
92 ret = sig0();
94 }
95 ret = sig0();
96 BOOST_CHECK_EQUAL(ret, false);
97 {
98 btcsignals::scoped_connection conn0{sig0.connect(ReturnTrue)};
99 btcsignals::scoped_connection conn1{sig0.connect(ReturnFalse)};
100 ret = sig0();
101 BOOST_CHECK_EQUAL(ret, true);
102 conn0.disconnect();
103 ret = sig0();
104 BOOST_CHECK_EQUAL(ret, false);
105 }
106 ret = sig0();
107 BOOST_CHECK_EQUAL(ret, false);
108}
109
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 is
116 * 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 */
121{
122 btcsignals::signal<void()> sig0;
123 std::atomic<uint32_t> val_det{0};
124 std::atomic<uint32_t> val_non_det{0};
125 auto conn0 = sig0.connect([&val_det] { val_det++; });
126
127 std::thread incrementor([&conn0, &sig0] {
128 for (int i = 0; i < 1000; i++) {
129 sig0();
130 }
131 // Because these calls are purposely happening on both threads at the
132 // same time, these must be asserts rather than BOOST_CHECKs to prevent
133 // a race inside of BOOST_CHECK itself (writing to the log).
134 assert(!sig0.empty());
135 assert(conn0.connected());
136 });
137
138 std::thread extra_increment_injector([&conn0, &sig0, &val_non_det] {
139 static constexpr size_t num_extra_conns{1000};
140 std::vector<btcsignals::scoped_connection> extra_conns;
141 extra_conns.reserve(num_extra_conns);
142 for (size_t i = 0; i < num_extra_conns; i++) {
143 BOOST_CHECK(!sig0.empty());
144 BOOST_CHECK(conn0.connected());
145 btcsignals::scoped_connection extra{sig0.connect([&val_non_det] { val_non_det++; })};
146 if (i % 2 == 0) {
147 extra_conns.emplace_back(std::move(extra));
148 }
149 sig0();
150 }
151 });
152 incrementor.join();
153 extra_increment_injector.join();
154 conn0.disconnect();
155 BOOST_CHECK(sig0.empty());
156
157 // sig0 will have been called 2000 times, and only the first connection did
158 // increment val_det, so it must be 2000.
159 BOOST_CHECK_EQUAL(val_det.load(), 2000);
160 // The number of connections that increment val_non_det is growing from 1
161 // 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 called
164 // exactly. The 500th Triangular Number gives a lower estimate.
165 // T_n=n(n+1)/2
166 BOOST_CHECK_GE(val_non_det.load(), 500 * 501 / 2);
167}
168
169/* Test that connection and disconnection works from within signal
170 * callbacks.
171 */
172BOOST_AUTO_TEST_CASE(recursion_safety)
173{
174 btcsignals::connection conn0, conn1, conn2;
175 btcsignals::signal<void()> sig0;
176 bool nonrecursive_callback_ran{false};
177 bool recursive_callback_ran{false};
178
179 conn0 = sig0.connect([&] {
180 BOOST_CHECK(!sig0.empty());
181 nonrecursive_callback_ran = true;
182 });
183 BOOST_CHECK(!nonrecursive_callback_ran);
184 sig0();
185 BOOST_CHECK(nonrecursive_callback_ran);
186 BOOST_CHECK(conn0.connected());
187
188 nonrecursive_callback_ran = false;
189 conn1 = sig0.connect([&] {
190 nonrecursive_callback_ran = true;
191 conn1.disconnect();
192 });
193 BOOST_CHECK(!nonrecursive_callback_ran);
194 BOOST_CHECK(conn0.connected());
195 BOOST_CHECK(conn1.connected());
196 sig0();
197 BOOST_CHECK(nonrecursive_callback_ran);
198 BOOST_CHECK(conn0.connected());
199 BOOST_CHECK(!conn1.connected());
200
201 nonrecursive_callback_ran = false;
202 conn1 = sig0.connect([&] {
203 conn2 = sig0.connect([&] {
204 BOOST_CHECK(conn0.connected());
205 recursive_callback_ran = true;
206 conn0.disconnect();
207 conn2.disconnect();
208 });
209 nonrecursive_callback_ran = true;
210 conn1.disconnect();
211 });
212 BOOST_CHECK(!nonrecursive_callback_ran);
213 BOOST_CHECK(!recursive_callback_ran);
214 BOOST_CHECK(conn0.connected());
215 BOOST_CHECK(conn1.connected());
216 BOOST_CHECK(!conn2.connected());
217 sig0();
218 BOOST_CHECK(nonrecursive_callback_ran);
219 BOOST_CHECK(!recursive_callback_ran);
220 BOOST_CHECK(conn0.connected());
221 BOOST_CHECK(!conn1.connected());
222 BOOST_CHECK(conn2.connected());
223 sig0();
224 BOOST_CHECK(recursive_callback_ran);
225 BOOST_CHECK(!conn0.connected());
226 BOOST_CHECK(!conn1.connected());
227 BOOST_CHECK(!conn2.connected());
228}
229
230/* Test that disconnection from another thread works in real time
231 */
232BOOST_AUTO_TEST_CASE(disconnect_thread_safety)
233{
234 btcsignals::connection conn0, conn1, conn2;
235 btcsignals::signal<void(int&)> sig0;
236 std::binary_semaphore done1{0};
237 std::binary_semaphore done2{0};
238 int val{0};
239
240 conn0 = sig0.connect([&](int&) {
241 conn1.disconnect();
242 done1.release();
243 done2.acquire();
244 });
245 conn1 = sig0.connect(IncrementCallback);
246 conn2 = sig0.connect(IncrementCallback);
247 std::thread thr([&] {
248 done1.acquire();
249 conn2.disconnect();
250 done2.release();
251 });
252 sig0(val);
253 thr.join();
254 BOOST_CHECK_EQUAL(val, 0);
255}
256
257
int ret
BOOST_AUTO_TEST_CASE(callback_order)
A combiner, which checks if at least one callback returned true.
Definition: btcsignals.h:42
void disconnect()
If a callback is associated with this connection, prevent it from being called in the future.
Definition: btcsignals.h:100
bool connected() const
Returns true if this connection was created by a signal and has not been disabled.
Definition: btcsignals.h:111
connection connect(Callable &&func) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition: btcsignals.h:236
bool empty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition: btcsignals.h:250
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
#define BOOST_CHECK(expr)
Definition: object.cpp:16
Basic testing setup.
Definition: setup_common.h:58
assert(!tx.IsCoinBase())