rippled
Loading...
Searching...
No Matches
ClosureCounter_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2017 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx/Env.h>
21
22#include <xrpld/core/ClosureCounter.h>
23
24#include <xrpl/beast/unit_test.h>
25
26#include <atomic>
27#include <chrono>
28#include <thread>
29
30namespace ripple {
31namespace test {
32
33//------------------------------------------------------------------------------
34
36{
37 // We're only using Env for its Journal. That Journal gives better
38 // coverage in unit tests.
40 *this,
42 nullptr,
44 beast::Journal j{env_.app().journal("ClosureCounter_test")};
45
46 void
48 {
49 // Build different kinds of ClosureCounters.
50 {
51 // Count closures that return void and take no arguments.
52 ClosureCounter<void> voidCounter;
53 BEAST_EXPECT(voidCounter.count() == 0);
54
55 int evidence = 0;
56 // Make sure voidCounter.wrap works with an rvalue closure.
57 auto wrapped = voidCounter.wrap([&evidence]() { ++evidence; });
58 BEAST_EXPECT(voidCounter.count() == 1);
59 BEAST_EXPECT(evidence == 0);
60 BEAST_EXPECT(wrapped);
61
62 // wrapped() should be callable with no arguments.
63 (*wrapped)();
64 BEAST_EXPECT(evidence == 1);
65 (*wrapped)();
66 BEAST_EXPECT(evidence == 2);
67
68 // Destroying the contents of wrapped should decrement voidCounter.
69 wrapped = std::nullopt;
70 BEAST_EXPECT(voidCounter.count() == 0);
71 }
72 {
73 // Count closures that return void and take one int argument.
75 BEAST_EXPECT(setCounter.count() == 0);
76
77 int evidence = 0;
78 // Make sure setCounter.wrap works with a non-const lvalue closure.
79 auto setInt = [&evidence](int i) { evidence = i; };
80 auto wrapped = setCounter.wrap(setInt);
81
82 BEAST_EXPECT(setCounter.count() == 1);
83 BEAST_EXPECT(evidence == 0);
84 BEAST_EXPECT(wrapped);
85
86 // wrapped() should be callable with one integer argument.
87 (*wrapped)(5);
88 BEAST_EXPECT(evidence == 5);
89 (*wrapped)(11);
90 BEAST_EXPECT(evidence == 11);
91
92 // Destroying the contents of wrapped should decrement setCounter.
93 wrapped = std::nullopt;
94 BEAST_EXPECT(setCounter.count() == 0);
95 }
96 {
97 // Count closures that return int and take two int arguments.
99 BEAST_EXPECT(sumCounter.count() == 0);
100
101 // Make sure sumCounter.wrap works with a const lvalue closure.
102 auto const sum = [](int ii, int jj) { return ii + jj; };
103 auto wrapped = sumCounter.wrap(sum);
104
105 BEAST_EXPECT(sumCounter.count() == 1);
106 BEAST_EXPECT(wrapped);
107
108 // wrapped() should be callable with two integers.
109 BEAST_EXPECT((*wrapped)(5, 2) == 7);
110 BEAST_EXPECT((*wrapped)(2, -8) == -6);
111
112 // Destroying the contents of wrapped should decrement sumCounter.
113 wrapped = std::nullopt;
114 BEAST_EXPECT(sumCounter.count() == 0);
115 }
116 }
117
118 // A class used to test argument passing.
120 {
121 public:
122 int copies = {0};
123 int moves = {0};
125
126 TrackedString() = delete;
127
128 explicit TrackedString(char const* rhs) : str(rhs)
129 {
130 }
131
132 // Copy constructor
134 : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str)
135 {
136 }
137
138 // Move constructor
140 : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str))
141 {
142 }
143
144 // Delete copy and move assignment.
146 operator=(TrackedString const& rhs) = delete;
147
148 // String concatenation
150 operator+=(char const* rhs)
151 {
152 str += rhs;
153 return *this;
154 }
155
156 friend TrackedString
157 operator+(TrackedString const& s, char const* rhs)
158 {
159 TrackedString ret{s};
160 ret.str += rhs;
161 return ret;
162 }
163 };
164
165 void
167 {
168 // Make sure a wrapped closure handles rvalue reference arguments
169 // correctly.
170 {
171 // Pass by value.
173 BEAST_EXPECT(strCounter.count() == 0);
174
175 auto wrapped =
176 strCounter.wrap([](TrackedString in) { return in += "!"; });
177
178 BEAST_EXPECT(strCounter.count() == 1);
179 BEAST_EXPECT(wrapped);
180
181 TrackedString const strValue("value");
182 TrackedString const result = (*wrapped)(strValue);
183 BEAST_EXPECT(result.copies == 2);
184 BEAST_EXPECT(result.moves == 1);
185 BEAST_EXPECT(result.str == "value!");
186 BEAST_EXPECT(strValue.str.size() == 5);
187 }
188 {
189 // Use a const lvalue argument.
191 BEAST_EXPECT(strCounter.count() == 0);
192
193 auto wrapped = strCounter.wrap(
194 [](TrackedString const& in) { return in + "!"; });
195
196 BEAST_EXPECT(strCounter.count() == 1);
197 BEAST_EXPECT(wrapped);
198
199 TrackedString const strConstLValue("const lvalue");
200 TrackedString const result = (*wrapped)(strConstLValue);
201 BEAST_EXPECT(result.copies == 1);
202 // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0
203 BEAST_EXPECT(result.str == "const lvalue!");
204 BEAST_EXPECT(strConstLValue.str.size() == 12);
205 }
206 {
207 // Use a non-const lvalue argument.
209 BEAST_EXPECT(strCounter.count() == 0);
210
211 auto wrapped =
212 strCounter.wrap([](TrackedString& in) { return in += "!"; });
213
214 BEAST_EXPECT(strCounter.count() == 1);
215 BEAST_EXPECT(wrapped);
216
217 TrackedString strLValue("lvalue");
218 TrackedString const result = (*wrapped)(strLValue);
219 BEAST_EXPECT(result.copies == 1);
220 BEAST_EXPECT(result.moves == 0);
221 BEAST_EXPECT(result.str == "lvalue!");
222 BEAST_EXPECT(strLValue.str == result.str);
223 }
224 {
225 // Use an rvalue argument.
227 BEAST_EXPECT(strCounter.count() == 0);
228
229 auto wrapped = strCounter.wrap([](TrackedString&& in) {
230 // Note that none of the compilers noticed that in was
231 // leaving scope. So, without intervention, they would
232 // do a copy for the return (June 2017). An explicit
233 // std::move() was required.
234 return std::move(in += "!");
235 });
236
237 BEAST_EXPECT(strCounter.count() == 1);
238 BEAST_EXPECT(wrapped);
239
240 // Make the string big enough to (probably) avoid the small string
241 // optimization.
242 TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz");
243 TrackedString const result = (*wrapped)(std::move(strRValue));
244 BEAST_EXPECT(result.copies == 0);
245 BEAST_EXPECT(result.moves == 1);
246 BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!");
247 BEAST_EXPECT(strRValue.str.size() == 0);
248 }
249 }
250
251 void
253 {
254 // Verify reference counting.
255 ClosureCounter<void> voidCounter;
256 BEAST_EXPECT(voidCounter.count() == 0);
257 {
258 auto wrapped1 = voidCounter.wrap([]() {});
259 BEAST_EXPECT(voidCounter.count() == 1);
260 {
261 // Copy should increase reference count.
262 auto wrapped2(wrapped1);
263 BEAST_EXPECT(voidCounter.count() == 2);
264 {
265 // Move should increase reference count.
266 auto wrapped3(std::move(wrapped2));
267 BEAST_EXPECT(voidCounter.count() == 3);
268 {
269 // An additional closure also increases count.
270 auto wrapped4 = voidCounter.wrap([]() {});
271 BEAST_EXPECT(voidCounter.count() == 4);
272 }
273 BEAST_EXPECT(voidCounter.count() == 3);
274 }
275 BEAST_EXPECT(voidCounter.count() == 2);
276 }
277 BEAST_EXPECT(voidCounter.count() == 1);
278 }
279 BEAST_EXPECT(voidCounter.count() == 0);
280
281 // Join with 0 count should not stall.
282 using namespace std::chrono_literals;
283 voidCounter.join("testWrap", 1ms, j);
284
285 // Wrapping a closure after join() should return std::nullopt.
286 BEAST_EXPECT(voidCounter.wrap([]() {}) == std::nullopt);
287 }
288
289 void
291 {
292 // Verify reference counting.
293 ClosureCounter<void> voidCounter;
294 BEAST_EXPECT(voidCounter.count() == 0);
295
296 auto wrapped = (voidCounter.wrap([]() {}));
297 BEAST_EXPECT(voidCounter.count() == 1);
298
299 // Calling join() now should stall, so do it on a different thread.
300 std::atomic<bool> threadExited{false};
301 std::thread localThread([&voidCounter, &threadExited, this]() {
302 // Should stall after calling join.
303 using namespace std::chrono_literals;
304 voidCounter.join("testWaitOnJoin", 1ms, j);
305 threadExited.store(true);
306 });
307
308 // Wait for the thread to call voidCounter.join().
309 while (!voidCounter.joined())
310 ;
311
312 // The thread should still be active after waiting 5 milliseconds.
313 // This is not a guarantee that join() stalled the thread, but it
314 // improves confidence.
315 using namespace std::chrono_literals;
317 BEAST_EXPECT(threadExited == false);
318
319 // Destroy the contents of wrapped and expect the thread to exit
320 // (asynchronously).
321 wrapped = std::nullopt;
322 BEAST_EXPECT(voidCounter.count() == 0);
323
324 // Wait for the thread to exit.
325 while (threadExited == false)
326 ;
327 localThread.join();
328 }
329
330public:
331 void
332 run() override
333 {
335 testArgs();
336 testWrap();
338 }
339};
340
341BEAST_DEFINE_TESTSUITE(ClosureCounter, core, ripple);
342
343} // namespace test
344} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
virtual beast::Journal journal(std::string const &name)=0
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.
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:121
Application & app()
Definition: Env.h:261
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
T size(T... args)
T sleep_for(T... args)