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