1#include <xrpl/beast/asio/io_latency_probe.h>
2#include <xrpl/beast/test/yield_to.h>
3#include <xrpl/beast/unit_test.h>
5#include <boost/asio/basic_waitable_timer.hpp>
6#include <boost/asio/deadline_timer.hpp>
7#include <boost/asio/executor_work_guard.hpp>
8#include <boost/asio/io_context.hpp>
16using namespace std::chrono_literals;
22 boost::asio::basic_waitable_timer<std::chrono::steady_clock>;
24#ifdef XRPL_RUNNING_IN_CI
35 struct measure_asio_timers
37 using duration =
typename Clock::duration;
38 using rep =
typename MeasureClock::duration::rep;
42 measure_asio_timers(duration interval = 100ms,
size_t num_samples = 50)
45 boost::asio::io_context
ios;
47 boost::asio::io_context::executor_type>>
48 work{boost::asio::make_work_guard(
ios)};
50 boost::asio::basic_waitable_timer<Clock> timer{
ios};
51 elapsed_times_.
reserve(num_samples);
56 boost::system::error_code wait_err;
60 auto const start{MeasureClock::now()};
62 timer.expires_after(interval);
63 timer.async_wait([&](boost::system::error_code
const& ec) {
66 auto const end{MeasureClock::now()};
72 cv.
wait(mainlock, [&done] {
return done; });
77 boost::asio::detail::throw_error(wait_err,
"wait");
85 for (
auto const& v : elapsed_times_)
87 sum +=
static_cast<double>(
88 std::chrono::duration_cast<D>(v).count());
90 return sum / elapsed_times_.
size();
97 return std::chrono::duration_cast<D>(
99 elapsed_times_.
begin(), elapsed_times_.
end()))
107 return std::chrono::duration_cast<D>(
109 elapsed_times_.
begin(), elapsed_times_.
end()))
122 boost::asio::io_context&
ios)
140 operator()(std::chrono::steady_clock::duration
const& elapsed)
150 boost::system::error_code ec;
154 timer.async_wait(yield[ec]);
155 if (!BEAST_EXPECTS(!ec, ec.message()))
157 BEAST_EXPECT(io_probe.durations_.size() == 1);
158 io_probe.probe_.cancel_async();
165 boost::system::error_code ec;
167 auto interval = 99ms;
168 auto probe_duration = 1s;
170 size_t expected_probe_count_max = (probe_duration / interval);
171 size_t expected_probe_count_min = expected_probe_count_max;
172#ifdef XRPL_RUNNING_IN_CI
175 measure_asio_timers<steady_clock> tt{interval};
180 expected_probe_count_min =
182 duration_cast<milliseconds>(probe_duration).count()) /
188 timer.async_wait(yield[ec]);
189 if (!BEAST_EXPECTS(!ec, ec.message()))
191 auto probes_seen = io_probe.durations_.size();
193 probes_seen >= (expected_probe_count_min - 1) &&
194 probes_seen <= (expected_probe_count_max + 1),
196 io_probe.probe_.cancel_async();
199 timer.expires_after(1s);
200 timer.async_wait(yield[ec]);
209 except<std::logic_error>([&io_probe]() { io_probe.start_one(); });
210 except<std::logic_error>([&io_probe]() { io_probe.start(); });
217 yield_to([&](boost::asio::yield_context& yield) {
225BEAST_DEFINE_TESTSUITE(io_latency_probe,
beast,
beast);
Measures handler latency on an io_context queue.
void sample(Handler &&handler)
Initiate continuous i/o latency sampling.
void sample_one(Handler &&handler)
Measure one sample of i/o latency.
Mix-in to support tests using asio coroutines.
boost::asio::io_context & get_io_context()
Return the io_context associated with the object.
void yield_to(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
log_os< char > log
Logging output stream.
testcase_t testcase
Memberspace for declaring test cases.
boost::asio::basic_waitable_timer< std::chrono::steady_clock > MyTimer
void testCanceled(boost::asio::yield_context &yield)
void testSampleOngoing(boost::asio::yield_context &yield)
void run() override
Runs the suite.
void testSampleOne(boost::asio::yield_context &yield)
T emplace_back(T... args)
void operator()(std::chrono::steady_clock::duration const &elapsed)
beast::io_latency_probe< std::chrono::steady_clock > probe_
std::vector< std::chrono::steady_clock::duration > durations_
test_sampler(std::chrono::milliseconds interval, boost::asio::io_context &ios)