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;
31
32public:
33 io_latency_probe(duration const& period, boost::asio::io_context& ios)
34 : m_count(1), m_period(period), m_ios(ios), m_timer(m_ios), m_cancel(false)
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 {
87 if (m_cancel)
88 throw std::logic_error("io_latency_probe is canceled");
89 boost::asio::post(m_ios, sample_op<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
90 }
91
96 template <class Handler>
97 void
98 sample(Handler&& handler)
99 {
101 if (m_cancel)
102 throw std::logic_error("io_latency_probe is canceled");
103 boost::asio::post(m_ios, sample_op<Handler>(std::forward<Handler>(handler), Clock::now(), true, this));
104 }
105
106private:
107 void
108 cancel(std::unique_lock<decltype(m_mutex)>& lock, bool wait)
109 {
110 if (!m_cancel)
111 {
112 --m_count;
113 m_cancel = true;
114 }
115
116 if (wait)
117 m_cond.wait(lock, [this] { return this->m_count == 0; });
118 }
119
120 void
122 {
124 ++m_count;
125 }
126
127 void
129 {
131 if (--m_count == 0)
133 }
134
135 template <class Handler>
137 {
138 Handler m_handler;
142
143 sample_op(Handler const& handler, time_point const& start, bool repeat, io_latency_probe* probe)
144 : m_handler(handler), m_start(start), m_repeat(repeat), m_probe(probe)
145 {
146 XRPL_ASSERT(
147 m_probe,
148 "beast::io_latency_probe::sample_op::sample_op : non-null "
149 "probe input");
150 m_probe->addref();
151 }
152
153 sample_op(sample_op&& from) noexcept
154 : m_handler(std::move(from.m_handler))
155 , m_start(from.m_start)
156 , m_repeat(from.m_repeat)
157 , m_probe(from.m_probe)
158 {
159 XRPL_ASSERT(
160 m_probe,
161 "beast::io_latency_probe::sample_op::sample_op(sample_op&&) : "
162 "non-null probe input");
163 from.m_probe = nullptr;
164 }
165
166 sample_op(sample_op const&) = delete;
168 operator=(sample_op const&) = delete;
169 sample_op&
170 operator=(sample_op&&) = delete;
171
173 {
174 if (m_probe)
175 m_probe->release();
176 }
177
178 void
180 {
181 if (!m_probe)
182 return;
183 typename Clock::time_point const now(Clock::now());
184 typename Clock::duration const elapsed(now - m_start);
185
186 m_handler(elapsed);
187
188 {
190 if (m_probe->m_cancel)
191 return;
192 }
193
194 if (m_repeat)
195 {
196 // Calculate when we want to sample again, and
197 // adjust for the expected latency.
198 //
199 typename Clock::time_point const when(now + m_probe->m_period - 2 * elapsed);
200
201 if (when <= now)
202 {
203 // The latency is too high to maintain the desired
204 // period so don't bother with a timer.
205 //
206 boost::asio::post(m_probe->m_ios, sample_op<Handler>(m_handler, now, m_repeat, m_probe));
207 }
208 else
209 {
210 m_probe->m_timer.expires_after(when - now);
212 }
213 }
214 }
215
216 void
217 operator()(boost::system::error_code const& ec)
218 {
219 if (!m_probe)
220 return;
221 typename Clock::time_point const now(Clock::now());
222 boost::asio::post(m_probe->m_ios, sample_op<Handler>(m_handler, now, m_repeat, m_probe));
223 }
224 };
225};
226
227} // 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