20#include <xrpl/beast/asio/io_latency_probe.h>
21#include <xrpl/beast/test/yield_to.h>
22#include <xrpl/beast/unit_test.h>
24#include <boost/asio/basic_waitable_timer.hpp>
25#include <boost/asio/deadline_timer.hpp>
26#include <boost/asio/executor_work_guard.hpp>
27#include <boost/asio/io_context.hpp>
35using namespace std::chrono_literals;
41 boost::asio::basic_waitable_timer<std::chrono::steady_clock>;
43#ifdef RIPPLED_RUNNING_IN_CI
54 struct measure_asio_timers
56 using duration =
typename Clock::duration;
57 using rep =
typename MeasureClock::duration::rep;
61 measure_asio_timers(duration interval = 100ms,
size_t num_samples = 50)
64 boost::asio::io_context
ios;
66 boost::asio::io_context::executor_type>>
67 work{boost::asio::make_work_guard(
ios)};
69 boost::asio::basic_waitable_timer<Clock> timer{
ios};
70 elapsed_times_.
reserve(num_samples);
75 boost::system::error_code wait_err;
79 auto const start{MeasureClock::now()};
81 timer.expires_after(interval);
82 timer.async_wait([&](boost::system::error_code
const& ec) {
85 auto const end{MeasureClock::now()};
91 cv.
wait(mainlock, [&done] {
return done; });
96 boost::asio::detail::throw_error(wait_err,
"wait");
104 for (
auto const& v : elapsed_times_)
106 sum +=
static_cast<double>(
107 std::chrono::duration_cast<D>(v).count());
109 return sum / elapsed_times_.
size();
116 return std::chrono::duration_cast<D>(
118 elapsed_times_.
begin(), elapsed_times_.
end()))
126 return std::chrono::duration_cast<D>(
128 elapsed_times_.
begin(), elapsed_times_.
end()))
141 boost::asio::io_context&
ios)
159 operator()(std::chrono::steady_clock::duration
const& elapsed)
169 boost::system::error_code ec;
173 timer.async_wait(yield[ec]);
174 if (!BEAST_EXPECTS(!ec, ec.message()))
176 BEAST_EXPECT(io_probe.durations_.size() == 1);
177 io_probe.probe_.cancel_async();
184 boost::system::error_code ec;
186 auto interval = 99ms;
187 auto probe_duration = 1s;
189 size_t expected_probe_count_max = (probe_duration / interval);
190 size_t expected_probe_count_min = expected_probe_count_max;
191#ifdef RIPPLED_RUNNING_IN_CI
194 measure_asio_timers<steady_clock> tt{interval};
199 expected_probe_count_min =
201 duration_cast<milliseconds>(probe_duration).count()) /
207 timer.async_wait(yield[ec]);
208 if (!BEAST_EXPECTS(!ec, ec.message()))
210 auto probes_seen = io_probe.durations_.size();
212 probes_seen >= (expected_probe_count_min - 1) &&
213 probes_seen <= (expected_probe_count_max + 1),
215 io_probe.probe_.cancel_async();
218 timer.expires_after(1s);
219 timer.async_wait(yield[ec]);
228 except<std::logic_error>([&io_probe]() { io_probe.start_one(); });
229 except<std::logic_error>([&io_probe]() { io_probe.start(); });
236 yield_to([&](boost::asio::yield_context& yield) {
244BEAST_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)