rippled
ClosureCounter.h
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 #ifndef RIPPLE_CORE_CLOSURE_COUNTER_H_INCLUDED
21 #define RIPPLE_CORE_CLOSURE_COUNTER_H_INCLUDED
22 
23 #include <ripple/basics/Log.h>
24 #include <boost/optional.hpp>
25 #include <atomic>
26 #include <condition_variable>
27 #include <mutex>
28 #include <type_traits>
29 
30 namespace ripple {
31 
32 // A class that does reference counting for postponed closures -- a closure
33 // who's execution is delayed by a timer or queue. The reference counting
34 // allows a Stoppable to assure that all such postponed closures are
35 // completed before the Stoppable declares itself stopped().
36 //
37 // Ret_t is the type that the counted closure returns.
38 // Args_t are the types of the arguments that will be passed to the closure.
39 template <typename Ret_t, typename... Args_t>
41 {
42 private:
43  std::mutex mutable mutex_{};
45  bool waitForClosures_{false}; // guard with mutex_
47 
48  // Increment the count.
51  {
52  ++closureCount_;
53  return *this;
54  }
55 
56  // Decrement the count. If we're stopping and the count drops to zero
57  // notify allClosuresDoneCond_.
60  {
61  // Even though closureCount_ is atomic, we decrement its value under
62  // a lock. This removes a small timing window that occurs if the
63  // waiting thread is handling a spurious wakeup when closureCount_
64  // drops to zero.
65  std::lock_guard lock{mutex_};
66 
67  // Update closureCount_. Notify if stopping and closureCount_ == 0.
68  if ((--closureCount_ == 0) && waitForClosures_)
70  return *this;
71  }
72 
73  // A private template class that helps count the number of closures
74  // in flight. This allows Stoppables to hold off declaring stopped()
75  // until all their postponed closures are dispatched.
76  template <typename Closure>
77  class Wrapper
78  {
79  private:
82 
83  static_assert(
84  std::is_same<decltype(closure_(std::declval<Args_t>()...)), Ret_t>::
85  value,
86  "Closure arguments don't match ClosureCounter Ret_t or Args_t");
87 
88  public:
89  Wrapper() = delete;
90 
91  Wrapper(Wrapper const& rhs)
92  : counter_(rhs.counter_), closure_(rhs.closure_)
93  {
94  ++counter_;
95  }
96 
97  Wrapper(Wrapper&& rhs) noexcept(
99  : counter_(rhs.counter_), closure_(std::move(rhs.closure_))
100  {
101  ++counter_;
102  }
103 
104  Wrapper(ClosureCounter& counter, Closure&& closure)
105  : counter_(counter), closure_(std::forward<Closure>(closure))
106  {
107  ++counter_;
108  }
109 
110  Wrapper&
111  operator=(Wrapper const& rhs) = delete;
112  Wrapper&
113  operator=(Wrapper&& rhs) = delete;
114 
116  {
117  --counter_;
118  }
119 
120  // Note that Args_t is not deduced, it is explicit. So Args_t&&
121  // would be an rvalue reference, not a forwarding reference. We
122  // want to forward exactly what the user declared.
123  Ret_t
124  operator()(Args_t... args)
125  {
126  return closure_(std::forward<Args_t>(args)...);
127  }
128  };
129 
130 public:
131  ClosureCounter() = default;
132  // Not copyable or movable. Outstanding counts would be hard to sort out.
133  ClosureCounter(ClosureCounter const&) = delete;
134 
136  operator=(ClosureCounter const&) = delete;
137 
140  {
141  using namespace std::chrono_literals;
142  join("ClosureCounter", 1s, debugLog());
143  }
144 
151  void
152  join(char const* name, std::chrono::milliseconds wait, beast::Journal j)
153  {
155  waitForClosures_ = true;
156  if (closureCount_ > 0)
157  {
159  lock, wait, [this] { return closureCount_ == 0; }))
160  {
161  if (auto stream = j.error())
162  stream << name << " waiting for ClosureCounter::join().";
164  lock, [this] { return closureCount_ == 0; });
165  }
166  }
167  }
168 
176  template <class Closure>
177  boost::optional<Wrapper<Closure>>
178  wrap(Closure&& closure)
179  {
180  boost::optional<Wrapper<Closure>> ret;
181 
182  std::lock_guard lock{mutex_};
183  if (!waitForClosures_)
184  ret.emplace(*this, std::forward<Closure>(closure));
185 
186  return ret;
187  }
188 
190  int
191  count() const
192  {
193  return closureCount_;
194  }
195 
202  bool
203  joined() const
204  {
205  std::lock_guard lock{mutex_};
206  return waitForClosures_;
207  }
208 };
209 
210 } // namespace ripple
211 
212 #endif // RIPPLE_CORE_CLOSURE_COUNTER_H_INCLUDED
std::is_same
ripple::ClosureCounter::ClosureCounter
ClosureCounter()=default
ripple::ClosureCounter::Wrapper::operator()
Ret_t operator()(Args_t... args)
Definition: ClosureCounter.h:124
ripple::ClosureCounter::mutex_
std::mutex mutex_
Definition: ClosureCounter.h:43
ripple::ClosureCounter::Wrapper
Definition: ClosureCounter.h:77
std::chrono::milliseconds
ripple::ClosureCounter::Wrapper::~Wrapper
~Wrapper()
Definition: ClosureCounter.h:115
std::lock_guard
STL class.
std::is_nothrow_move_constructible
ripple::ClosureCounter::Wrapper::Wrapper
Wrapper()=delete
ripple::debugLog
beast::Journal debugLog()
Returns a debug journal.
Definition: Log.cpp:452
ripple::ClosureCounter::operator--
ClosureCounter & operator--()
Definition: ClosureCounter.h:59
ripple::ClosureCounter::~ClosureCounter
~ClosureCounter()
Destructor verifies all in-flight closures are complete.
Definition: ClosureCounter.h:139
ripple::ClosureCounter::allClosuresDoneCond_
std::condition_variable allClosuresDoneCond_
Definition: ClosureCounter.h:44
ripple::ClosureCounter::waitForClosures_
bool waitForClosures_
Definition: ClosureCounter.h:45
std::unique_lock
STL class.
ripple::ClosureCounter::operator++
ClosureCounter & operator++()
Definition: ClosureCounter.h:50
ripple::ClosureCounter::Wrapper::closure_
std::remove_reference_t< Closure > closure_
Definition: ClosureCounter.h:81
ripple::ClosureCounter::Wrapper::counter_
ClosureCounter & counter_
Definition: ClosureCounter.h:80
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::ClosureCounter
Definition: ClosureCounter.h:40
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::condition_variable::wait
T wait(T... args)
atomic
ripple::ClosureCounter::wrap
boost::optional< Wrapper< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Definition: ClosureCounter.h:178
ripple::ClosureCounter::operator=
ClosureCounter & operator=(ClosureCounter const &)=delete
std::condition_variable::wait_for
T wait_for(T... args)
ripple::ClosureCounter::Wrapper::operator=
Wrapper & operator=(Wrapper const &rhs)=delete
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std::remove_reference_t< Closure >
std
STL namespace.
ripple::ClosureCounter::Wrapper::Wrapper
Wrapper(Wrapper const &rhs)
Definition: ClosureCounter.h:91
condition_variable
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:152
mutex
ripple::ClosureCounter::count
int count() const
Current number of Closures outstanding.
Definition: ClosureCounter.h:191
ripple::ClosureCounter::closureCount_
std::atomic< int > closureCount_
Definition: ClosureCounter.h:46
ripple::ClosureCounter::Wrapper::Wrapper
Wrapper(Wrapper &&rhs) noexcept(std::is_nothrow_move_constructible< Closure >::value)
Definition: ClosureCounter.h:97
ripple::ClosureCounter::Wrapper::Wrapper
Wrapper(ClosureCounter &counter, Closure &&closure)
Definition: ClosureCounter.h:104
type_traits
std::condition_variable::notify_all
T notify_all(T... args)
ripple::ClosureCounter::joined
bool joined() const
Returns true if this has been joined.
Definition: ClosureCounter.h:203