xrpld
Loading...
Searching...
No Matches
ClosureCounter.h
1#pragma once
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/Journal.h>
5
6#include <atomic>
7#include <chrono>
8#include <condition_variable>
9#include <mutex>
10#include <optional>
11#include <type_traits>
12
13namespace xrpl {
14
36template <typename Ret, typename... Args>
38{
39private:
42 bool waitForClosures_{false}; // guard with mutex_
44
45 // Increment the count.
48 {
50 return *this;
51 }
52
53 // Decrement the count. If we're stopping and the count drops to zero
54 // notify allClosuresDoneCond_.
57 {
58 // Even though closureCount_ is atomic, we decrement its value under
59 // a lock. This removes a small timing window that occurs if the
60 // waiting thread is handling a spurious wakeup when closureCount_
61 // drops to zero.
62 std::scoped_lock const lock{mutex_};
63
64 // Update closureCount_. Notify if stopping and closureCount_ == 0.
65 if ((--closureCount_ == 0) && waitForClosures_)
66 allClosuresDoneCond_.notify_all();
67 return *this;
68 }
69
70 // A private template class that helps count the number of closures
71 // in flight. This allows callers to block until all their postponed
72 // closures are dispatched.
73 template <typename Closure>
75 {
76 private:
79
80 static_assert(
82 "Closure arguments don't match ClosureCounter Ret or Args");
83
84 public:
85 Substitute() = delete;
86
88 {
89 ++counter_;
90 }
91
97
99 ClosureCounter& counter,
100 Closure&& closure) // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
101 : counter_(counter), closure_(std::forward<Closure>(closure))
102 {
103 ++counter_;
104 }
105
107 operator=(Substitute const& rhs) = delete;
109 operator=(Substitute&& rhs) = delete;
110
112 {
113 --counter_;
114 }
115
116 // Note that Args is not deduced, it is explicit. So Args&&
117 // would be an rvalue reference, not a forwarding reference. We
118 // want to forward exactly what the user declared.
119 Ret
120 operator()(Args... args)
121 {
122 return closure_(std::forward<Args>(args)...);
123 }
124 };
125
126public:
127 ClosureCounter() = default;
128 // Not copyable or movable. Outstanding counts would be hard to sort out.
130
132 operator=(ClosureCounter const&) = delete;
133
138 {
139 using namespace std::chrono_literals;
140 join("ClosureCounter", 1s, debugLog());
141 }
142
150 void
152 {
154 waitForClosures_ = true;
155 if (closureCount_ > 0)
156 {
157 if (!allClosuresDoneCond_.wait_for(lock, wait, [this] { return closureCount_ == 0; }))
158 {
159 if (auto stream = j.error())
160 stream << name << " waiting for ClosureCounter::join().";
161 allClosuresDoneCond_.wait(lock, [this] { return closureCount_ == 0; });
162 }
163 }
164 }
165
174 template <class Closure>
176 wrap(Closure&& closure)
177 {
179
180 std::scoped_lock const lock{mutex_};
181 if (!waitForClosures_)
182 ret.emplace(*this, std::forward<Closure>(closure));
183
184 return ret;
185 }
186
190 int
191 count() const
192 {
193 return closureCount_;
194 }
195
203 bool
204 joined() const
205 {
206 std::scoped_lock const lock{mutex_};
207 return waitForClosures_;
208 }
209};
210
211} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream error() const
Definition Journal.h:362
std::remove_reference_t< Closure > closure_
Substitute & operator=(Substitute &&rhs)=delete
Substitute(Substitute &&rhs) noexcept(std::is_nothrow_move_constructible_v< Closure >)
Substitute(ClosureCounter &counter, Closure &&closure)
Substitute & operator=(Substitute const &rhs)=delete
Substitute(Substitute const &rhs)
The role of a ClosureCounter is to assist in shutdown by letting callers wait for the completion of c...
ClosureCounter(ClosureCounter const &)=delete
void join(char const *name, std::chrono::milliseconds wait, beast::Journal j)
Returns once all counted in-flight closures are destroyed.
std::condition_variable allClosuresDoneCond_
std::atomic< int > closureCount_
~ClosureCounter()
Destructor verifies all in-flight closures are complete.
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
ClosureCounter()=default
ClosureCounter & operator++()
ClosureCounter & operator=(ClosureCounter const &)=delete
bool joined() const
Returns true if this has been joined.
int count() const
Current number of Closures outstanding.
ClosureCounter & operator--()
T emplace(T... args)
T forward(T... args)
T is_nothrow_move_constructible_v
T is_same_v
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:399