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#include <xrpld/core/ClosureCounter.h>
22#include <xrpl/beast/unit_test.h>
23
24#include <atomic>
25#include <chrono>
26#include <thread>
27
28namespace ripple {
29namespace test {
30
31//------------------------------------------------------------------------------
32
34{
35 // We're only using Env for its Journal. That Journal gives better
36 // coverage in unit tests.
38 *this,
40 nullptr,
42 beast::Journal j{env_.app().journal("ClosureCounter_test")};
43
44 void
46 {
47 // Build different kinds of ClosureCounters.
48 {
49 // Count closures that return void and take no arguments.
50 ClosureCounter<void> voidCounter;
51 BEAST_EXPECT(voidCounter.count() == 0);
52
53 int evidence = 0;
54 // Make sure voidCounter.wrap works with an rvalue closure.
55 auto wrapped = voidCounter.wrap([&evidence]() { ++evidence; });
56 BEAST_EXPECT(voidCounter.count() == 1);
57 BEAST_EXPECT(evidence == 0);
58 BEAST_EXPECT(wrapped);
59
60 // wrapped() should be callable with no arguments.
61 (*wrapped)();
62 BEAST_EXPECT(evidence == 1);
63 (*wrapped)();
64 BEAST_EXPECT(evidence == 2);
65
66 // Destroying the contents of wrapped should decrement voidCounter.
67 wrapped = std::nullopt;
68 BEAST_EXPECT(voidCounter.count() == 0);
69 }
70 {
71 // Count closures that return void and take one int argument.
73 BEAST_EXPECT(setCounter.count() == 0);
74
75 int evidence = 0;
76 // Make sure setCounter.wrap works with a non-const lvalue closure.
77 auto setInt = [&evidence](int i) { evidence = i; };
78 auto wrapped = setCounter.wrap(setInt);
79
80 BEAST_EXPECT(setCounter.count() == 1);
81 BEAST_EXPECT(evidence == 0);
82 BEAST_EXPECT(wrapped);
83
84 // wrapped() should be callable with one integer argument.
85 (*wrapped)(5);
86 BEAST_EXPECT(evidence == 5);
87 (*wrapped)(11);
88 BEAST_EXPECT(evidence == 11);
89
90 // Destroying the contents of wrapped should decrement setCounter.
91 wrapped = std::nullopt;
92 BEAST_EXPECT(setCounter.count() == 0);
93 }
94 {
95 // Count closures that return int and take two int arguments.
97 BEAST_EXPECT(sumCounter.count() == 0);
98
99 // Make sure sumCounter.wrap works with a const lvalue closure.
100 auto const sum = [](int ii, int jj) { return ii + jj; };
101 auto wrapped = sumCounter.wrap(sum);
102
103 BEAST_EXPECT(sumCounter.count() == 1);
104 BEAST_EXPECT(wrapped);
105
106 // wrapped() should be callable with two integers.
107 BEAST_EXPECT((*wrapped)(5, 2) == 7);
108 BEAST_EXPECT((*wrapped)(2, -8) == -6);
109
110 // Destroying the contents of wrapped should decrement sumCounter.
111 wrapped = std::nullopt;
112 BEAST_EXPECT(sumCounter.count() == 0);
113 }
114 }
115
116 // A class used to test argument passing.
118 {
119 public:
120 int copies = {0};
121 int moves = {0};
123
124 TrackedString() = delete;
125
126 explicit TrackedString(char const* rhs) : str(rhs)
127 {
128 }
129
130 // Copy constructor
132 : copies(rhs.copies + 1), moves(rhs.moves), str(rhs.str)
133 {
134 }
135
136 // Move constructor
138 : copies(rhs.copies), moves(rhs.moves + 1), str(std::move(rhs.str))
139 {
140 }
141
142 // Delete copy and move assignment.
144 operator=(TrackedString const& rhs) = delete;
145
146 // String concatenation
148 operator+=(char const* rhs)
149 {
150 str += rhs;
151 return *this;
152 }
153
154 friend TrackedString
155 operator+(TrackedString const& s, char const* rhs)
156 {
157 TrackedString ret{s};
158 ret.str += rhs;
159 return ret;
160 }
161 };
162
163 void
165 {
166 // Make sure a wrapped closure handles rvalue reference arguments
167 // correctly.
168 {
169 // Pass by value.
171 BEAST_EXPECT(strCounter.count() == 0);
172
173 auto wrapped =
174 strCounter.wrap([](TrackedString in) { return in += "!"; });
175
176 BEAST_EXPECT(strCounter.count() == 1);
177 BEAST_EXPECT(wrapped);
178
179 TrackedString const strValue("value");
180 TrackedString const result = (*wrapped)(strValue);
181 BEAST_EXPECT(result.copies == 2);
182 BEAST_EXPECT(result.moves == 1);
183 BEAST_EXPECT(result.str == "value!");
184 BEAST_EXPECT(strValue.str.size() == 5);
185 }
186 {
187 // Use a const lvalue argument.
189 BEAST_EXPECT(strCounter.count() == 0);
190
191 auto wrapped = strCounter.wrap(
192 [](TrackedString const& in) { return in + "!"; });
193
194 BEAST_EXPECT(strCounter.count() == 1);
195 BEAST_EXPECT(wrapped);
196
197 TrackedString const strConstLValue("const lvalue");
198 TrackedString const result = (*wrapped)(strConstLValue);
199 BEAST_EXPECT(result.copies == 1);
200 // BEAST_EXPECT (result.moves == ?); // moves VS == 1, gcc == 0
201 BEAST_EXPECT(result.str == "const lvalue!");
202 BEAST_EXPECT(strConstLValue.str.size() == 12);
203 }
204 {
205 // Use a non-const lvalue argument.
207 BEAST_EXPECT(strCounter.count() == 0);
208
209 auto wrapped =
210 strCounter.wrap([](TrackedString& in) { return in += "!"; });
211
212 BEAST_EXPECT(strCounter.count() == 1);
213 BEAST_EXPECT(wrapped);
214
215 TrackedString strLValue("lvalue");
216 TrackedString const result = (*wrapped)(strLValue);
217 BEAST_EXPECT(result.copies == 1);
218 BEAST_EXPECT(result.moves == 0);
219 BEAST_EXPECT(result.str == "lvalue!");
220 BEAST_EXPECT(strLValue.str == result.str);
221 }
222 {
223 // Use an rvalue argument.
225 BEAST_EXPECT(strCounter.count() == 0);
226
227 auto wrapped = strCounter.wrap([](TrackedString&& in) {
228 // Note that none of the compilers noticed that in was
229 // leaving scope. So, without intervention, they would
230 // do a copy for the return (June 2017). An explicit
231 // std::move() was required.
232 return std::move(in += "!");
233 });
234
235 BEAST_EXPECT(strCounter.count() == 1);
236 BEAST_EXPECT(wrapped);
237
238 // Make the string big enough to (probably) avoid the small string
239 // optimization.
240 TrackedString strRValue("rvalue abcdefghijklmnopqrstuvwxyz");
241 TrackedString const result = (*wrapped)(std::move(strRValue));
242 BEAST_EXPECT(result.copies == 0);
243 BEAST_EXPECT(result.moves == 1);
244 BEAST_EXPECT(result.str == "rvalue abcdefghijklmnopqrstuvwxyz!");
245 BEAST_EXPECT(strRValue.str.size() == 0);
246 }
247 }
248
249 void
251 {
252 // Verify reference counting.
253 ClosureCounter<void> voidCounter;
254 BEAST_EXPECT(voidCounter.count() == 0);
255 {
256 auto wrapped1 = voidCounter.wrap([]() {});
257 BEAST_EXPECT(voidCounter.count() == 1);
258 {
259 // Copy should increase reference count.
260 auto wrapped2(wrapped1);
261 BEAST_EXPECT(voidCounter.count() == 2);
262 {
263 // Move should increase reference count.
264 auto wrapped3(std::move(wrapped2));
265 BEAST_EXPECT(voidCounter.count() == 3);
266 {
267 // An additional closure also increases count.
268 auto wrapped4 = voidCounter.wrap([]() {});
269 BEAST_EXPECT(voidCounter.count() == 4);
270 }
271 BEAST_EXPECT(voidCounter.count() == 3);
272 }
273 BEAST_EXPECT(voidCounter.count() == 2);
274 }
275 BEAST_EXPECT(voidCounter.count() == 1);
276 }
277 BEAST_EXPECT(voidCounter.count() == 0);
278
279 // Join with 0 count should not stall.
280 using namespace std::chrono_literals;
281 voidCounter.join("testWrap", 1ms, j);
282
283 // Wrapping a closure after join() should return std::nullopt.
284 BEAST_EXPECT(voidCounter.wrap([]() {}) == std::nullopt);
285 }
286
287 void
289 {
290 // Verify reference counting.
291 ClosureCounter<void> voidCounter;
292 BEAST_EXPECT(voidCounter.count() == 0);
293
294 auto wrapped = (voidCounter.wrap([]() {}));
295 BEAST_EXPECT(voidCounter.count() == 1);
296
297 // Calling join() now should stall, so do it on a different thread.
298 std::atomic<bool> threadExited{false};
299 std::thread localThread([&voidCounter, &threadExited, this]() {
300 // Should stall after calling join.
301 using namespace std::chrono_literals;
302 voidCounter.join("testWaitOnJoin", 1ms, j);
303 threadExited.store(true);
304 });
305
306 // Wait for the thread to call voidCounter.join().
307 while (!voidCounter.joined())
308 ;
309
310 // The thread should still be active after waiting 5 milliseconds.
311 // This is not a guarantee that join() stalled the thread, but it
312 // improves confidence.
313 using namespace std::chrono_literals;
315 BEAST_EXPECT(threadExited == false);
316
317 // Destroy the contents of wrapped and expect the thread to exit
318 // (asynchronously).
319 wrapped = std::nullopt;
320 BEAST_EXPECT(voidCounter.count() == 0);
321
322 // Wait for the thread to exit.
323 while (threadExited == false)
324 ;
325 localThread.join();
326 }
327
328public:
329 void
330 run() override
331 {
333 testArgs();
334 testWrap();
336 }
337};
338
339BEAST_DEFINE_TESTSUITE(ClosureCounter, core, ripple);
340
341} // namespace test
342} // 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:118
Application & app()
Definition: Env.h:256
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:26
static auto sum(TCollection const &col)
Definition: BookStep.cpp:999
T size(T... args)
T sleep_for(T... args)