Bitcoin Core
32.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
test
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
5
#include <
test/util/setup_common.h
>
6
#include <
util/btcsignals.h
>
7
8
#include <boost/test/unit_test.hpp>
9
10
#include <semaphore>
11
12
namespace
{
13
14
15
void
IncrementCallback(
int
& val)
16
{
17
val++;
18
}
19
void
SquareCallback(
int
& val)
20
{
21
val *= val;
22
}
23
24
bool
ReturnTrue()
25
{
26
return
true
;
27
}
28
bool
ReturnFalse()
29
{
30
return
false
;
31
}
32
33
}
// anonymous namespace
34
35
BOOST_FIXTURE_TEST_SUITE
(btcsignals_tests,
BasicTestingSetup
)
36
37
/* Callbacks should always be executed in the order in which they were added
38
*/
39
BOOST_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
50
BOOST_AUTO_TEST_CASE
(disconnects)
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
{
76
btcsignals::scoped_connection
scope(conn0);
77
}
78
BOOST_CHECK
(!conn0.connected());
79
BOOST_CHECK
(sig0.
empty
());
80
sig0(val);
81
BOOST_CHECK_EQUAL
(val, 6);
82
}
83
84
BOOST_AUTO_TEST_CASE
(any_of_combiner)
85
{
86
btcsignals::signal
<bool(),
btcsignals::any_of
> sig0;
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();
93
BOOST_CHECK_EQUAL
(
ret
,
true
);
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
*/
120
BOOST_AUTO_TEST_CASE
(thread_safety)
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
*/
172
BOOST_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
*/
232
BOOST_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
258
BOOST_AUTO_TEST_SUITE_END
()
ret
int ret
Definition
bitcoin-cli.cpp:1691
btcsignals.h
btcsignals::any_of
A combiner, which checks if at least one callback returned true.
Definition
btcsignals.h:42
btcsignals::connection
Definition
btcsignals.h:58
btcsignals::connection::disconnect
void disconnect()
If a callback is associated with this connection, prevent it from being called in the future.
Definition
btcsignals.h:100
btcsignals::connection::connected
bool connected() const
Returns true if this connection was created by a signal and has not been disabled.
Definition
btcsignals.h:111
btcsignals::scoped_connection
Definition
btcsignals.h:121
btcsignals::scoped_connection::disconnect
void disconnect()
Definition
btcsignals.h:136
btcsignals::signal
Definition
btcsignals.h:152
btcsignals::signal::connect
connection connect(Callable &&func) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition
btcsignals.h:236
btcsignals::signal::empty
bool empty() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Definition
btcsignals.h:250
BOOST_FIXTURE_TEST_SUITE
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(ipc_tests)
Definition
ipc_tests.cpp:202
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition
object.cpp:17
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition
object.cpp:16
setup_common.h
BasicTestingSetup
Basic testing setup.
Definition
setup_common.h:58
assert
assert(!tx.IsCoinBase())
Generated on
for Bitcoin Core by
1.18.0