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