xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
test
beast
beast_io_latency_probe_test.cpp
1
#include <xrpl/beast/asio/io_latency_probe.h>
2
#include <xrpl/beast/test/yield_to.h>
3
#include <xrpl/beast/unit_test/suite.h>
4
5
#include <boost/asio/basic_waitable_timer.hpp>
6
#include <boost/asio/executor_work_guard.hpp>
// IWYU pragma: keep
7
#include <boost/asio/io_context.hpp>
8
#include <boost/asio/spawn.hpp>
9
#include <boost/system/detail/error_code.hpp>
10
11
#include <
algorithm
>
// IWYU pragma: keep
12
#include <
chrono
>
13
#include <
condition_variable
>
// IWYU pragma: keep
14
#include <
cstddef
>
15
#include <
functional
>
16
#include <
mutex
>
// IWYU pragma: keep
17
#include <
optional
>
// IWYU pragma: keep
18
#include <
stdexcept
>
19
#include <
string
>
20
#include <
thread
>
// IWYU pragma: keep
21
#include <
vector
>
22
23
using namespace
std::chrono_literals;
24
25
class
io_latency_probe_test
:
public
beast::unit_test::Suite
,
public
beast::test::EnableYieldTo
26
{
27
using
MyTimer
= boost::asio::basic_waitable_timer<std::chrono::steady_clock>;
28
29
#ifdef XRPL_RUNNING_IN_CI
37
template
<
class
Clock,
class
MeasureClock = std::chrono::high_resolution_clock>
38
struct
MeasureAsioTimers
39
{
40
using
duration = Clock::duration;
41
using
rep = MeasureClock::duration::rep;
42
43
std::vector<duration>
elapsedTimes;
44
45
MeasureAsioTimers(duration interval = 100ms,
size_t
numSamples = 50)
46
{
47
using namespace
std::chrono
;
48
boost::asio::io_context
ios
;
49
std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
>
50
work{boost::asio::make_work_guard(
ios
)};
51
std::thread
worker{[&] {
ios
.run(); }};
52
boost::asio::basic_waitable_timer<Clock> timer{
ios
};
53
elapsedTimes.
reserve
(numSamples);
54
std::mutex
mtx;
55
std::unique_lock<std::mutex>
mainlock{mtx};
56
std::condition_variable
cv;
57
bool
done =
false
;
58
boost::system::error_code waitErr;
59
60
while
(--numSamples > 0u)
61
{
62
auto
const
start{MeasureClock::now()};
63
done =
false
;
64
timer.expires_after(interval);
65
timer.async_wait([&](boost::system::error_code
const
& ec) {
66
if
(ec)
67
waitErr = ec;
68
auto
const
end
{MeasureClock::now()};
69
elapsedTimes.
emplace_back
(
end
- start);
70
std::scoped_lock
const
lk{mtx};
71
done =
true
;
72
cv.
notify_one
();
73
});
74
cv.
wait
(mainlock, [&done] {
return
done; });
75
}
76
work.
reset
();
77
worker.join();
78
if
(waitErr)
79
boost::asio::detail::throw_error(waitErr,
"wait"
);
80
}
81
82
template
<
class
D>
83
auto
84
getMean()
85
{
86
double
sum = {0};
87
for
(
auto
const
& v : elapsedTimes)
88
{
89
sum +=
static_cast<
double
>
(
std::chrono::duration_cast<D>
(v).count());
90
}
91
return
sum / elapsedTimes.size();
92
}
93
94
template
<
class
D>
95
auto
96
getMax()
97
{
98
return
std::chrono::duration_cast<D>
(*
std::ranges::max_element
(elapsedTimes)).count();
99
}
100
101
template
<
class
D>
102
auto
103
getMin()
104
{
105
return
std::chrono::duration_cast<D>
(*
std::ranges::min_element
(elapsedTimes)).count();
106
}
107
};
108
#endif
109
110
struct
TestSampler
111
{
112
beast::IOLatencyProbe<std::chrono::steady_clock>
probe
;
113
std::vector<std::chrono::steady_clock::duration>
durations
;
114
115
TestSampler
(
std::chrono::milliseconds
interval, boost::asio::io_context&
ios
)
116
:
probe
(interval,
ios
)
117
{
118
}
119
120
void
121
start
()
122
{
123
probe
.sample(
std::ref
(*
this
));
124
}
125
126
void
127
startOne
()
128
{
129
probe
.sampleOne(
std::ref
(*
this
));
130
}
131
132
void
133
operator()
(std::chrono::steady_clock::duration
const
& elapsed)
134
{
135
durations
.push_back(elapsed);
136
}
137
};
138
139
void
140
testSampleOne
(boost::asio::yield_context& yield)
141
{
142
testcase
<<
"sample one"
;
143
boost::system::error_code ec;
144
TestSampler
ioProbe{100ms,
getIoContext
()};
145
ioProbe.
startOne
();
146
MyTimer
timer{
getIoContext
(), 1s};
147
timer.async_wait(yield[ec]);
148
if
(!BEAST_EXPECTS(!ec, ec.message()))
149
return
;
150
BEAST_EXPECT(ioProbe.
durations
.
size
() == 1);
151
ioProbe.
probe
.
cancelAsync
();
152
}
153
154
void
155
testSampleOngoing
(boost::asio::yield_context& yield)
156
{
157
testcase
<<
"sample ongoing"
;
158
boost::system::error_code ec;
159
using namespace
std::chrono
;
160
auto
interval = 99ms;
161
auto
probeDuration = 1s;
162
163
size_t
const
expectedProbeCountMax = (probeDuration / interval);
164
// NOLINTNEXTLINE(misc-const-correctness)
165
size_t
expectedProbeCountMin = expectedProbeCountMax;
166
#ifdef XRPL_RUNNING_IN_CI
167
// adjust min expected based on measurements
168
// if running in CI/VM environment
169
MeasureAsioTimers<steady_clock> tt{interval};
170
log
<<
"measured mean for timers: "
<< tt.getMean<
milliseconds
>() <<
"ms\n"
;
171
log
<<
"measured max for timers: "
<< tt.getMax<
milliseconds
>() <<
"ms\n"
;
172
expectedProbeCountMin =
173
static_cast<
size_t
>
(
duration_cast<milliseconds>
(probeDuration).count()) /
174
static_cast<
size_t
>
(tt.getMean<
milliseconds
>());
175
#endif
176
TestSampler
ioProbe{interval,
getIoContext
()};
177
ioProbe.
start
();
178
MyTimer
timer{
getIoContext
(), probeDuration};
179
timer.async_wait(yield[ec]);
180
if
(!BEAST_EXPECTS(!ec, ec.message()))
181
return
;
182
auto
probesSeen = ioProbe.
durations
.
size
();
183
BEAST_EXPECTS(
184
probesSeen >= (expectedProbeCountMin - 1) && probesSeen <= (expectedProbeCountMax + 1),
185
std::string
(
"probe count is "
) +
std::to_string
(probesSeen));
186
ioProbe.
probe
.
cancelAsync
();
187
// wait again in order to flush the remaining
188
// probes from the work queue
189
timer.expires_after(1s);
190
timer.async_wait(yield[ec]);
191
}
192
193
void
194
testCanceled
(boost::asio::yield_context& yield)
195
{
196
testcase
<<
"canceled"
;
197
TestSampler
ioProbe{100ms,
getIoContext
()};
198
ioProbe.
probe
.
cancelAsync
();
199
except<std::logic_error>
([&ioProbe]() { ioProbe.
startOne
(); });
200
except<std::logic_error>
([&ioProbe]() { ioProbe.
start
(); });
201
}
202
203
public
:
204
void
205
run
()
override
206
{
207
yieldTo
([&](boost::asio::yield_context& yield) {
208
testSampleOne
(yield);
209
testSampleOngoing
(yield);
210
testCanceled
(yield);
211
});
212
}
213
};
214
215
BEAST_DEFINE_TESTSUITE(io_latency_probe,
beast
,
beast
);
algorithm
std::ios
std::string
chrono
beast::IOLatencyProbe
Measures handler latency on an io_context queue.
Definition
io_latency_probe.h:22
beast::IOLatencyProbe::cancelAsync
void cancelAsync()
Definition
io_latency_probe.h:77
beast::test::EnableYieldTo
Mix-in to support tests using asio coroutines.
Definition
yield_to.h:30
beast::test::EnableYieldTo::getIoContext
boost::asio::io_context & getIoContext()
Return the io_context associated with the object.
Definition
yield_to.h:67
beast::test::EnableYieldTo::yieldTo
void yieldTo(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
Definition
yield_to.h:107
beast::unit_test::Suite
A testsuite class.
Definition
suite.h:52
beast::unit_test::Suite::except
bool except(F &&f, String const &reason)
Definition
suite.h:465
beast::unit_test::Suite::log
LogOs< char > log
Logging output stream.
Definition
suite.h:150
beast::unit_test::Suite::testcase
TestcaseT testcase
Memberspace for declaring test cases.
Definition
suite.h:155
io_latency_probe_test
Definition
beast_io_latency_probe_test.cpp:26
io_latency_probe_test::MyTimer
boost::asio::basic_waitable_timer< std::chrono::steady_clock > MyTimer
Definition
beast_io_latency_probe_test.cpp:27
io_latency_probe_test::testCanceled
void testCanceled(boost::asio::yield_context &yield)
Definition
beast_io_latency_probe_test.cpp:194
io_latency_probe_test::testSampleOngoing
void testSampleOngoing(boost::asio::yield_context &yield)
Definition
beast_io_latency_probe_test.cpp:155
io_latency_probe_test::run
void run() override
Runs the suite.
Definition
beast_io_latency_probe_test.cpp:205
io_latency_probe_test::testSampleOne
void testSampleOne(boost::asio::yield_context &yield)
Definition
beast_io_latency_probe_test.cpp:140
condition_variable
cstddef
std::chrono::duration_cast
T duration_cast(T... args)
std::chrono::milliseconds
std::vector::emplace_back
T emplace_back(T... args)
std::end
T end(T... args)
functional
std::ranges::max_element
T max_element(T... args)
std::ranges::min_element
T min_element(T... args)
mutex
beast
Definition
base_uint.h:697
std::chrono
std::condition_variable::notify_one
T notify_one(T... args)
optional
std::ref
T ref(T... args)
std::vector::reserve
T reserve(T... args)
std::optional::reset
T reset(T... args)
std::scoped_lock
std::vector::size
T size(T... args)
stdexcept
string
io_latency_probe_test::TestSampler
Definition
beast_io_latency_probe_test.cpp:111
io_latency_probe_test::TestSampler::TestSampler
TestSampler(std::chrono::milliseconds interval, boost::asio::io_context &ios)
Definition
beast_io_latency_probe_test.cpp:115
io_latency_probe_test::TestSampler::durations
std::vector< std::chrono::steady_clock::duration > durations
Definition
beast_io_latency_probe_test.cpp:113
io_latency_probe_test::TestSampler::start
void start()
Definition
beast_io_latency_probe_test.cpp:121
io_latency_probe_test::TestSampler::operator()
void operator()(std::chrono::steady_clock::duration const &elapsed)
Definition
beast_io_latency_probe_test.cpp:133
io_latency_probe_test::TestSampler::startOne
void startOne()
Definition
beast_io_latency_probe_test.cpp:127
io_latency_probe_test::TestSampler::probe
beast::IOLatencyProbe< std::chrono::steady_clock > probe
Definition
beast_io_latency_probe_test.cpp:112
thread
std::to_string
T to_string(T... args)
std::unique_lock
vector
std::condition_variable::wait
T wait(T... args)
Generated by
1.17.0