rippled
Loading...
Searching...
No Matches
io_latency_probe.h
1#pragma once
2
3#include <xrpl/beast/utility/instrumentation.h>
4
5#include <boost/asio/basic_waitable_timer.hpp>
6#include <boost/asio/io_context.hpp>
7#include <boost/asio/post.hpp>
8
9#include <chrono>
10#include <condition_variable>
11#include <mutex>
12#include <stdexcept>
13
14namespace beast {
15
17template <class Clock>
19{
20private:
21 using duration = typename Clock::duration;
22 using time_point = typename Clock::time_point;
23
28 boost::asio::io_context& m_ios;
29 boost::asio::basic_waitable_timer<std::chrono::steady_clock> m_timer;
30 bool m_cancel{false};
31
32public:
33 io_latency_probe(duration const& period, boost::asio::io_context& ios)
34 : m_period(period), m_ios(ios), m_timer(m_ios)
35 {
36 }
37
39 {
40 std::unique_lock<decltype(m_mutex)> lock(m_mutex);
41 cancel(lock, true);
42 }
43
46 boost::asio::io_context&
48 {
49 return m_ios;
50 }
51
52 boost::asio::io_context const&
54 {
55 return m_ios;
56 }
63 void
65 {
66 std::unique_lock<decltype(m_mutex)> lock(m_mutex);
67 cancel(lock, true);
68 }
69
70 void
72 {
73 std::unique_lock<decltype(m_mutex)> lock(m_mutex);
74 cancel(lock, false);
75 }
82 template <class Handler>
83 void
84 sample_one(Handler&& handler)
85 {
86 std::lock_guard const lock(m_mutex);
87 if (m_cancel)
88 throw std::logic_error("io_latency_probe is canceled");
89 boost::asio::post(
90 m_ios, sample_op<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
91 }
92
97 template <class Handler>
98 void
99 sample(Handler&& handler)
100 {
101 std::lock_guard const lock(m_mutex);
102 if (m_cancel)
103 throw std::logic_error("io_latency_probe is canceled");
104 boost::asio::post(
105 m_ios, sample_op<Handler>(std::forward<Handler>(handler), Clock::now(), true, this));
106 }
107
108private:
109 void
110 cancel(std::unique_lock<decltype(m_mutex)>& lock, bool wait)
111 {
112 if (!m_cancel)
113 {
114 --m_count;
115 m_cancel = true;
116 }
117
118 if (wait)
119 m_cond.wait(lock, [this] { return this->m_count == 0; });
120 }
121
122 void
124 {
125 std::lock_guard const lock(m_mutex);
126 ++m_count;
127 }
128
129 void
131 {
132 std::lock_guard const lock(m_mutex);
133 if (--m_count == 0)
135 }
136
137 template <class Handler>
139 {
140 Handler m_handler;
144
146 Handler const& handler,
147 time_point const& start,
148 bool repeat,
149 io_latency_probe* probe)
150 : m_handler(handler), m_start(start), m_repeat(repeat), m_probe(probe)
151 {
152 XRPL_ASSERT(
153 m_probe,
154 "beast::io_latency_probe::sample_op::sample_op : non-null "
155 "probe input");
156 m_probe->addref();
157 }
158
159 sample_op(sample_op&& from) noexcept
160 : m_handler(std::move(from.m_handler))
161 , m_start(from.m_start)
162 , m_repeat(from.m_repeat)
163 , m_probe(from.m_probe)
164 {
165 XRPL_ASSERT(
166 m_probe,
167 "beast::io_latency_probe::sample_op::sample_op(sample_op&&) : "
168 "non-null probe input");
169 from.m_probe = nullptr;
170 }
171
172 sample_op(sample_op const&) = delete;
174 operator=(sample_op const&) = delete;
175 sample_op&
176 operator=(sample_op&&) = delete;
177
179 {
180 if (m_probe)
181 m_probe->release();
182 }
183
184 void
186 {
187 if (!m_probe)
188 return;
189 typename Clock::time_point const now(Clock::now());
190 typename Clock::duration const elapsed(now - m_start);
191
192 m_handler(elapsed);
193
194 {
195 std::lock_guard const lock(m_probe->m_mutex);
196 if (m_probe->m_cancel)
197 return;
198 }
199
200 if (m_repeat)
201 {
202 // Calculate when we want to sample again, and
203 // adjust for the expected latency.
204 //
205 typename Clock::time_point const when(now + m_probe->m_period - 2 * elapsed);
206
207 if (when <= now)
208 {
209 // The latency is too high to maintain the desired
210 // period so don't bother with a timer.
211 //
212 boost::asio::post(
214 }
215 else
216 {
217 m_probe->m_timer.expires_after(when - now);
218 m_probe->m_timer.async_wait(
220 }
221 }
222 }
223
224 void
225 operator()(boost::system::error_code const& ec)
226 {
227 if (!m_probe)
228 return;
229 typename Clock::time_point const now(Clock::now());
230 boost::asio::post(
232 }
233 };
234};
235
236} // namespace beast
Measures handler latency on an io_context queue.
void sample(Handler &&handler)
Initiate continuous i/o latency sampling.
std::recursive_mutex m_mutex
boost::asio::basic_waitable_timer< std::chrono::steady_clock > m_timer
void cancel()
Cancel all pending i/o.
boost::asio::io_context const & get_io_context() const
void sample_one(Handler &&handler)
Measure one sample of i/o latency.
typename Clock::duration duration
boost::asio::io_context & get_io_context()
Return the io_context associated with the latency probe.
boost::asio::io_context & m_ios
void cancel(std::unique_lock< decltype(m_mutex)> &lock, bool wait)
std::condition_variable_any m_cond
io_latency_probe(duration const &period, boost::asio::io_context &ios)
typename Clock::time_point time_point
T is_same_v
sample_op(Handler const &handler, time_point const &start, bool repeat, io_latency_probe *probe)
sample_op operator=(sample_op const &)=delete
sample_op & operator=(sample_op &&)=delete
sample_op(sample_op &&from) noexcept
void operator()(boost::system::error_code const &ec)
sample_op(sample_op const &)=delete