xrpld
Loading...
Searching...
No Matches
ClosureCounter_test.cpp
1#include <test/jtx/Env.h>
2#include <test/jtx/envconfig.h>
3
4#include <xrpl/beast/unit_test/suite.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/core/ClosureCounter.h>
7
8#include <atomic>
9#include <optional>
10#include <thread>
11#include <utility>
12
13namespace xrpl::test {
14
15//------------------------------------------------------------------------------
16
18{
19 // We're only using Env for its Journal. That Journal gives better
20 // coverage in unit tests.
22 beast::Journal j_{env_.app().getJournal("ClosureCounter_test")};
23
24 void
26 {
27 // Build different kinds of ClosureCounters.
28 {
29 // Count closures that return void and take no arguments.
30 ClosureCounter<void> voidCounter;
31 BEAST_EXPECT(voidCounter.count() == 0);
32
33 int evidence = 0;
34 // Make sure voidCounter.wrap works with an rvalue closure.
35 auto wrapped = voidCounter.wrap([&evidence]() { ++evidence; });
36 BEAST_EXPECT(voidCounter.count() == 1);
37 BEAST_EXPECT(evidence == 0);
38 BEAST_EXPECT(wrapped);
39
40 // wrapped() should be callable with no arguments.
41 (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access)
42 BEAST_EXPECT(evidence == 1);
43 (*wrapped)(); // NOLINT(bugprone-unchecked-optional-access)
44 BEAST_EXPECT(evidence == 2);
45
46 // Destroying the contents of wrapped should decrement voidCounter.
47 wrapped = std::nullopt;
48 BEAST_EXPECT(voidCounter.count() == 0);
49 }
50 {
51 // Count closures that return void and take one int argument.
53 BEAST_EXPECT(setCounter.count() == 0);
54
55 int evidence = 0;
56 // Make sure setCounter.wrap works with a non-const lvalue closure.
57 auto setInt = [&evidence](int i) { evidence = i; };
58 auto wrapped = setCounter.wrap(setInt);
59
60 BEAST_EXPECT(setCounter.count() == 1);
61 BEAST_EXPECT(evidence == 0);
62 BEAST_EXPECT(wrapped);
63
64 // wrapped() should be callable with one integer argument.
65 (*wrapped)(5); // NOLINT(bugprone-unchecked-optional-access)
66 BEAST_EXPECT(evidence == 5);
67 (*wrapped)(11); // NOLINT(bugprone-unchecked-optional-access)
68 BEAST_EXPECT(evidence == 11);
69
70 // Destroying the contents of wrapped should decrement setCounter.
71 wrapped = std::nullopt;
72 BEAST_EXPECT(setCounter.count() == 0);
73 }
74 {
75 // Count closures that return int and take two int arguments.
77 BEAST_EXPECT(sumCounter.count() == 0);
78
79 // Make sure sumCounter.wrap works with a const lvalue closure.
80 auto const sum = [](int ii, int jj) { return ii + jj; };
81 auto wrapped = sumCounter.wrap(sum);
82
83 BEAST_EXPECT(sumCounter.count() == 1);
84 BEAST_EXPECT(wrapped);
85
86 // wrapped() should be callable with two integers.
87 BEAST_EXPECT((*wrapped)(5, 2) == 7); // NOLINT(bugprone-unchecked-optional-access)
88 BEAST_EXPECT((*wrapped)(2, -8) == -6); // NOLINT(bugprone-unchecked-optional-access)
89
90 // Destroying the contents of wrapped should decrement sumCounter.
91 wrapped = std::nullopt;
92 BEAST_EXPECT(sumCounter.count() == 0);
93 }
94 }
95
96 // A class used to test argument passing.
98 {
99 public:
100 int copies = {0};
101 int moves = {0};
103
104 TrackedString() = delete;
105
106 explicit TrackedString(char const* rhs) : str(rhs)
107 {
108 }
109
110 // Copy constructor
112 : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str)
113 {
114 }
115
116 // Move constructor
118 : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str))
119 {
120 }
121
122 // Delete copy and move assignment.
124 operator=(TrackedString const& rhs) = delete;
125
126 // String concatenation
128 operator+=(char const* rhs)
129 {
130 str += rhs;
131 return *this;
132 }
133
134 friend TrackedString
135 operator+(TrackedString const& s, char const* rhs)
136 {
137 TrackedString ret{s};
138 ret.str += rhs;
139 return ret;
140 }
141 };
142
143 void
145 {
146 // Make sure a wrapped closure handles rvalue reference arguments
147 // correctly.
148 {
149 // Pass by value.
151 BEAST_EXPECT(strCounter.count() == 0);
152
153 auto wrapped = strCounter.wrap([](TrackedString in) { return in += "!"; });
154
155 BEAST_EXPECT(strCounter.count() == 1);
156 BEAST_EXPECT(wrapped);
157
158 TrackedString const strValue("value");
159 TrackedString const result =
160 (*wrapped)(strValue); // NOLINT(bugprone-unchecked-optional-access)
161 BEAST_EXPECT(result.copies == 2);
162 BEAST_EXPECT(result.moves == 1);
163 BEAST_EXPECT(result.str == "value!");
164 BEAST_EXPECT(strValue.str.size() == 5);
165 }
166 {
167 // Use a const lvalue argument.
169 BEAST_EXPECT(strCounter.count() == 0);
170
171 auto wrapped = strCounter.wrap([](TrackedString const& in) { return in + "!"; });
172
173 BEAST_EXPECT(strCounter.count() == 1);
174 BEAST_EXPECT(wrapped);
175
176 TrackedString const strConstLValue("const lvalue");
177 TrackedString const result =
178 (*wrapped)(strConstLValue); // NOLINT(bugprone-unchecked-optional-access)
179 BEAST_EXPECT(result.copies == 1);
180 // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0
181 BEAST_EXPECT(result.str == "const lvalue!");
182 BEAST_EXPECT(strConstLValue.str.size() == 12);
183 }
184 {
185 // Use a non-const lvalue argument.
187 BEAST_EXPECT(strCounter.count() == 0);
188
189 auto wrapped = strCounter.wrap([](TrackedString& in) { return in += "!"; });
190
191 BEAST_EXPECT(strCounter.count() == 1);
192 BEAST_EXPECT(wrapped);
193
194 TrackedString strLValue("lvalue");
195 TrackedString const result =
196 (*wrapped)(strLValue); // NOLINT(bugprone-unchecked-optional-access)
197 BEAST_EXPECT(result.copies == 1);
198 BEAST_EXPECT(result.moves == 0);
199 BEAST_EXPECT(result.str == "lvalue!");
200 BEAST_EXPECT(strLValue.str == result.str);
201 }
202 {
203 // Use an rvalue argument.
205 BEAST_EXPECT(strCounter.count() == 0);
206
207 auto wrapped = strCounter.wrap([](TrackedString&& in) {
208 // Note that kNone of the compilers noticed that in was
209 // leaving scope. So, without intervention, they would
210 // do a copy for the return (June 2017). An explicit
211 // std::move() was required.
212 in += "!";
213 return std::move(in);
214 });
215
216 BEAST_EXPECT(strCounter.count() == 1);
217 BEAST_EXPECT(wrapped);
218
219 // Make the string big enough to (probably) avoid the small string optimization.
220 TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz");
221 TrackedString const result =
222 (*wrapped)(std::move(strRValue)); // NOLINT(bugprone-unchecked-optional-access)
223 BEAST_EXPECT(result.copies == 0);
224 BEAST_EXPECT(result.moves == 1);
225 BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!");
226 BEAST_EXPECT(strRValue.str.empty()); // NOLINT(bugprone-use-after-move)
227 }
228 }
229
230 void
232 {
233 // Verify reference counting.
234 ClosureCounter<void> voidCounter;
235 BEAST_EXPECT(voidCounter.count() == 0);
236 {
237 auto wrapped1 = voidCounter.wrap([]() {});
238 BEAST_EXPECT(voidCounter.count() == 1);
239 {
240 // Copy should increase reference count.
241 auto wrapped2(wrapped1);
242 BEAST_EXPECT(voidCounter.count() == 2);
243 {
244 // Move should increase reference count.
245 auto wrapped3(std::move(wrapped2));
246 BEAST_EXPECT(voidCounter.count() == 3);
247 {
248 // An additional closure also increases count.
249 auto wrapped4 = voidCounter.wrap([]() {});
250 BEAST_EXPECT(voidCounter.count() == 4);
251 }
252 BEAST_EXPECT(voidCounter.count() == 3);
253 }
254 BEAST_EXPECT(voidCounter.count() == 2);
255 }
256 BEAST_EXPECT(voidCounter.count() == 1);
257 }
258 BEAST_EXPECT(voidCounter.count() == 0);
259
260 // Join with 0 count should not stall.
261 using namespace std::chrono_literals;
262 voidCounter.join("testWrap", 1ms, j_);
263
264 // Wrapping a closure after join() should return std::nullopt.
265 BEAST_EXPECT(voidCounter.wrap([]() {}) == std::nullopt);
266 }
267
268 void
270 {
271 // Verify reference counting.
272 ClosureCounter<void> voidCounter;
273 BEAST_EXPECT(voidCounter.count() == 0);
274
275 auto wrapped = (voidCounter.wrap([]() {}));
276 BEAST_EXPECT(voidCounter.count() == 1);
277
278 // Calling join() now should stall, so do it on a different thread.
279 std::atomic<bool> threadExited{false};
280 std::thread localThread([&voidCounter, &threadExited, this]() {
281 // Should stall after calling join.
282 using namespace std::chrono_literals;
283 voidCounter.join("testWaitOnJoin", 1ms, j_);
284 threadExited.store(true);
285 });
286
287 // Wait for the thread to call voidCounter.join().
288 while (!voidCounter.joined())
289 ;
290
291 // The thread should still be active after waiting 5 milliseconds.
292 // This is not a guarantee that join() stalled the thread, but it
293 // improves confidence.
294 using namespace std::chrono_literals;
296 BEAST_EXPECT(threadExited == false);
297
298 // Destroy the contents of wrapped and expect the thread to exit
299 // (asynchronously).
300 wrapped = std::nullopt;
301 BEAST_EXPECT(voidCounter.count() == 0);
302
303 // Wait for the thread to exit.
304 while (!threadExited)
305 ;
306 localThread.join();
307 }
308
309public:
310 void
311 run() override
312 {
314 testArgs();
315 testWrap();
317 }
318};
319
321
322} // namespace xrpl::test
A generic endpoint for log messages.
Definition Journal.h:38
A testsuite class.
Definition suite.h:50
The role of a ClosureCounter is to assist in shutdown by letting callers wait for the completion of c...
void join(char const *name, std::chrono::milliseconds wait, beast::Journal j)
Returns once all counted in-flight closures are destroyed.
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
bool joined() const
Returns true if this has been joined.
int count() const
Current number of Closures outstanding.
friend TrackedString operator+(TrackedString const &s, char const *rhs)
TrackedString & operator=(TrackedString const &rhs)=delete
void run() override
Runs the suite.
A transaction testing environment.
Definition Env.h:143
T empty(T... args)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:28
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static auto sum(TCollection const &col)
Definition BookStep.cpp:993
T size(T... args)
T sleep_for(T... args)
T store(T... args)