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;
20 using MyTimer = boost::asio::basic_waitable_timer<std::chrono::steady_clock>;
22#ifdef XRPL_RUNNING_IN_CI
30 template <
class Clock,
class MeasureClock = std::chrono::high_resolution_clock>
31 struct measure_asio_timers
33 using duration =
typename Clock::duration;
34 using rep =
typename MeasureClock::duration::rep;
38 measure_asio_timers(duration interval = 100ms,
size_t num_samples = 50)
41 boost::asio::io_context
ios;
43 boost::asio::make_work_guard(
ios)};
45 boost::asio::basic_waitable_timer<Clock> timer{
ios};
46 elapsed_times_.
reserve(num_samples);
51 boost::system::error_code wait_err;
55 auto const start{MeasureClock::now()};
57 timer.expires_after(interval);
58 timer.async_wait([&](boost::system::error_code
const& ec) {
61 auto const end{MeasureClock::now()};
67 cv.
wait(mainlock, [&done] {
return done; });
72 boost::asio::detail::throw_error(wait_err,
"wait");
80 for (
auto const& v : elapsed_times_)
82 sum +=
static_cast<double>(std::chrono::duration_cast<D>(v).count());
84 return sum / elapsed_times_.
size();
127 operator()(std::chrono::steady_clock::duration
const& elapsed)
137 boost::system::error_code ec;
141 timer.async_wait(yield[ec]);
142 if (!BEAST_EXPECTS(!ec, ec.message()))
144 BEAST_EXPECT(io_probe.durations_.size() == 1);
145 io_probe.probe_.cancel_async();
152 boost::system::error_code ec;
154 auto interval = 99ms;
155 auto probe_duration = 1s;
157 size_t expected_probe_count_max = (probe_duration / interval);
158 size_t expected_probe_count_min = expected_probe_count_max;
159#ifdef XRPL_RUNNING_IN_CI
162 measure_asio_timers<steady_clock> tt{interval};
163 log <<
"measured mean for timers: " << tt.getMean<
milliseconds>() <<
"ms\n";
164 log <<
"measured max for timers: " << tt.getMax<
milliseconds>() <<
"ms\n";
165 expected_probe_count_min =
static_cast<size_t>(duration_cast<milliseconds>(probe_duration).count()) /
171 timer.async_wait(yield[ec]);
172 if (!BEAST_EXPECTS(!ec, ec.message()))
174 auto probes_seen = io_probe.durations_.size();
176 probes_seen >= (expected_probe_count_min - 1) && probes_seen <= (expected_probe_count_max + 1),
178 io_probe.probe_.cancel_async();
181 timer.expires_after(1s);
182 timer.async_wait(yield[ec]);
191 except<std::logic_error>([&io_probe]() { io_probe.start_one(); });
192 except<std::logic_error>([&io_probe]() { io_probe.start(); });
199 yield_to([&](boost::asio::yield_context& yield) {
207BEAST_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)