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.
50  {
51  ++closureCount_;
52  return *this;
53  }
54 
55  // Decrement the count. If we're stopping and the count drops to zero
56  // notify allClosuresDoneCond_.
58  {
59  // Even though closureCount_ is atomic, we decrement its value under
60  // a lock. This removes a small timing window that occurs if the
61  // waiting thread is handling a spurious wakeup when closureCount_
62  // drops to zero.
63  std::lock_guard lock {mutex_};
64 
65  // Update closureCount_. Notify if stopping and closureCount_ == 0.
66  if ((--closureCount_ == 0) && waitForClosures_)
68  return *this;
69  }
70 
71  // A private template class that helps count the number of closures
72  // in flight. This allows Stoppables to hold off declaring stopped()
73  // until all their postponed closures are dispatched.
74  template <typename Closure>
75  class Wrapper
76  {
77  private:
80 
81  static_assert (
82  std::is_same<decltype(
83  closure_(std::declval<Args_t>()...)), Ret_t>::value,
84  "Closure arguments don't match ClosureCounter Ret_t or Args_t");
85 
86  public:
87  Wrapper() = delete;
88 
89  Wrapper (Wrapper const& rhs)
90  : counter_ (rhs.counter_)
91  , closure_ (rhs.closure_)
92  {
93  ++counter_;
94  }
95 
96  Wrapper (Wrapper&& rhs) noexcept(
98  : counter_ (rhs.counter_)
99  , closure_ (std::move (rhs.closure_))
100  {
101  ++counter_;
102  }
103 
104  Wrapper (ClosureCounter& counter, Closure&& closure)
105  : counter_ (counter)
106  , closure_ (std::forward<Closure> (closure))
107  {
108  ++counter_;
109  }
110 
111  Wrapper& operator=(Wrapper const& rhs) = delete;
112  Wrapper& operator=(Wrapper&& rhs) = delete;
113 
115  {
116  --counter_;
117  }
118 
119  // Note that Args_t is not deduced, it is explicit. So Args_t&&
120  // would be an rvalue reference, not a forwarding reference. We
121  // want to forward exactly what the user declared.
122  Ret_t operator ()(Args_t... args)
123  {
124  return closure_ (std::forward<Args_t>(args)...);
125  }
126  };
127 
128 public:
129  ClosureCounter() = default;
130  // Not copyable or movable. Outstanding counts would be hard to sort out.
131  ClosureCounter (ClosureCounter const&) = delete;
132 
133  ClosureCounter& operator=(ClosureCounter const&) = delete;
134 
137  {
138  using namespace std::chrono_literals;
139  join ("ClosureCounter", 1s, debugLog());
140  }
141 
148  void join (char const* name,
150  {
152  waitForClosures_ = true;
153  if (closureCount_ > 0)
154  {
156  lock, wait, [this] { return closureCount_ == 0; }))
157  {
158  if (auto stream = j.error())
159  stream << name
160  << " waiting for ClosureCounter::join().";
162  lock, [this] { return closureCount_ == 0; });
163  }
164  }
165  }
166 
174  template <class Closure>
175  boost::optional<Wrapper<Closure>>
176  wrap (Closure&& closure)
177  {
178  boost::optional<Wrapper<Closure>> ret;
179 
180  std::lock_guard lock {mutex_};
181  if (! waitForClosures_)
182  ret.emplace (*this, std::forward<Closure> (closure));
183 
184  return ret;
185  }
186 
188  int count() const
189  {
190  return closureCount_;
191  }
192 
199  bool joined() const
200  {
201  std::lock_guard lock {mutex_};
202  return waitForClosures_;
203  }
204 };
205 
206 } // ripple
207 
208 #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:122
ripple::ClosureCounter::mutex_
std::mutex mutex_
Definition: ClosureCounter.h:43
ripple::ClosureCounter::Wrapper
Definition: ClosureCounter.h:75
std::chrono::milliseconds
ripple::ClosureCounter::Wrapper::~Wrapper
~Wrapper()
Definition: ClosureCounter.h:114
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:417
ripple::ClosureCounter::operator--
ClosureCounter & operator--()
Definition: ClosureCounter.h:57
ripple::ClosureCounter::~ClosureCounter
~ClosureCounter()
Destructor verifies all in-flight closures are complete.
Definition: ClosureCounter.h:136
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:49
ripple::ClosureCounter::Wrapper::closure_
std::remove_reference_t< Closure > closure_
Definition: ClosureCounter.h:79
ripple::ClosureCounter::Wrapper::counter_
ClosureCounter & counter_
Definition: ClosureCounter.h:78
beast::Journal::error
Stream error() const
Definition: Journal.h:307
ripple::ClosureCounter
Definition: ClosureCounter.h:40
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
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:176
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:89
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:148
mutex
ripple::ClosureCounter::count
int count() const
Current number of Closures outstanding.
Definition: ClosureCounter.h:188
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:96
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:199