xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
include
xrpl
core
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
13
namespace
xrpl
{
14
36
template
<
typename
Ret,
typename
... Args>
37
class
ClosureCounter
38
{
39
private
:
40
std::mutex
mutable
mutex_
;
41
std::condition_variable
allClosuresDoneCond_
;
// guard with mutex_
42
bool
waitForClosures_
{
false
};
// guard with mutex_
43
std::atomic<int>
closureCount_
{0};
44
45
// Increment the count.
46
ClosureCounter
&
47
operator++
()
48
{
49
++
closureCount_
;
50
return
*
this
;
51
}
52
53
// Decrement the count. If we're stopping and the count drops to zero
54
// notify allClosuresDoneCond_.
55
ClosureCounter
&
56
operator--
()
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>
74
class
Substitute
75
{
76
private
:
77
ClosureCounter
&
counter_
;
78
std::remove_reference_t<Closure>
closure_
{};
79
80
static_assert
(
81
std::is_same_v<decltype(closure_(std::declval<Args>
()...)), Ret>,
82
"Closure arguments don't match ClosureCounter Ret or Args"
);
83
84
public
:
85
Substitute
() =
delete
;
86
87
Substitute
(
Substitute
const
& rhs) :
counter_
(rhs.
counter_
),
closure_
(rhs.
closure_
)
88
{
89
++
counter_
;
90
}
91
92
Substitute
(
Substitute
&& rhs)
noexcept
(
std::is_nothrow_move_constructible_v<Closure>
)
93
:
counter_
(rhs.
counter_
),
closure_
(
std
::move(rhs.
closure_
))
94
{
95
++
counter_
;
96
}
97
98
Substitute
(
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
106
Substitute
&
107
operator=
(
Substitute
const
& rhs) =
delete
;
108
Substitute
&
109
operator=
(
Substitute
&& rhs) =
delete
;
110
111
~Substitute
()
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
126
public
:
127
ClosureCounter
() =
default
;
128
// Not copyable or movable. Outstanding counts would be hard to sort out.
129
ClosureCounter
(
ClosureCounter
const
&) =
delete
;
130
131
ClosureCounter
&
132
operator=
(
ClosureCounter
const
&) =
delete
;
133
137
~ClosureCounter
()
138
{
139
using namespace
std::chrono_literals;
140
join
(
"ClosureCounter"
, 1s,
debugLog
());
141
}
142
150
void
151
join
(
char
const
* name,
std::chrono::milliseconds
wait,
beast::Journal
j)
152
{
153
std::unique_lock<std::mutex>
lock{
mutex_
};
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>
175
std::optional<Substitute<Closure>
>
176
wrap
(Closure&& closure)
177
{
178
std::optional<Substitute<Closure>
> ret;
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
atomic
chrono
beast::Journal
A generic endpoint for log messages.
Definition
Journal.h:44
beast::Journal::error
Stream error() const
Definition
Journal.h:362
xrpl::ClosureCounter::Substitute
Definition
ClosureCounter.h:75
xrpl::ClosureCounter::Substitute::closure_
std::remove_reference_t< Closure > closure_
Definition
ClosureCounter.h:78
xrpl::ClosureCounter::Substitute::operator=
Substitute & operator=(Substitute &&rhs)=delete
xrpl::ClosureCounter::Substitute::counter_
ClosureCounter & counter_
Definition
ClosureCounter.h:77
xrpl::ClosureCounter::Substitute::Substitute
Substitute(Substitute &&rhs) noexcept(std::is_nothrow_move_constructible_v< Closure >)
Definition
ClosureCounter.h:92
xrpl::ClosureCounter::Substitute::Substitute
Substitute(ClosureCounter &counter, Closure &&closure)
Definition
ClosureCounter.h:98
xrpl::ClosureCounter::Substitute::operator=
Substitute & operator=(Substitute const &rhs)=delete
xrpl::ClosureCounter::Substitute::Substitute
Substitute(Substitute const &rhs)
Definition
ClosureCounter.h:87
xrpl::ClosureCounter::Substitute::Substitute
Substitute()=delete
xrpl::ClosureCounter::Substitute::operator()
Ret operator()(Args... args)
Definition
ClosureCounter.h:120
xrpl::ClosureCounter::Substitute::~Substitute
~Substitute()
Definition
ClosureCounter.h:111
xrpl::ClosureCounter
The role of a ClosureCounter is to assist in shutdown by letting callers wait for the completion of c...
Definition
ClosureCounter.h:38
xrpl::ClosureCounter::ClosureCounter
ClosureCounter(ClosureCounter const &)=delete
xrpl::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:151
xrpl::ClosureCounter< void >::allClosuresDoneCond_
std::condition_variable allClosuresDoneCond_
Definition
ClosureCounter.h:41
xrpl::ClosureCounter< void >::mutex_
std::mutex mutex_
Definition
ClosureCounter.h:40
xrpl::ClosureCounter< void >::waitForClosures_
bool waitForClosures_
Definition
ClosureCounter.h:42
xrpl::ClosureCounter< void >::closureCount_
std::atomic< int > closureCount_
Definition
ClosureCounter.h:43
xrpl::ClosureCounter::~ClosureCounter
~ClosureCounter()
Destructor verifies all in-flight closures are complete.
Definition
ClosureCounter.h:137
xrpl::ClosureCounter::wrap
std::optional< Substitute< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Definition
ClosureCounter.h:176
xrpl::ClosureCounter::ClosureCounter
ClosureCounter()=default
xrpl::ClosureCounter::operator++
ClosureCounter & operator++()
Definition
ClosureCounter.h:47
xrpl::ClosureCounter::operator=
ClosureCounter & operator=(ClosureCounter const &)=delete
xrpl::ClosureCounter::joined
bool joined() const
Returns true if this has been joined.
Definition
ClosureCounter.h:204
xrpl::ClosureCounter::count
int count() const
Current number of Closures outstanding.
Definition
ClosureCounter.h:191
xrpl::ClosureCounter::operator--
ClosureCounter & operator--()
Definition
ClosureCounter.h:56
condition_variable
std::chrono::milliseconds
std::optional::emplace
T emplace(T... args)
std::forward
T forward(T... args)
std::is_nothrow_move_constructible_v
T is_nothrow_move_constructible_v
std::is_same_v
T is_same_v
mutex
std
STL namespace.
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
xrpl::debugLog
beast::Journal debugLog()
Returns a debug journal.
Definition
Log.cpp:399
optional
std::remove_reference_t
std::scoped_lock
type_traits
std::unique_lock
Generated by
1.17.0