xrpld
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 <cstddef>
12#include <mutex>
13#include <stdexcept>
14
15namespace beast {
16
20template <class Clock>
22{
23private:
24 using duration = Clock::duration;
25 using time_point = Clock::time_point;
26
31 boost::asio::io_context& ios_;
32 boost::asio::basic_waitable_timer<std::chrono::steady_clock> timer_;
33 bool cancel_{false};
34
35public:
36 IOLatencyProbe(duration const& period, boost::asio::io_context& ios)
37 : period_(period), ios_(ios), timer_(ios_)
38 {
39 }
40
42 {
43 std::unique_lock<decltype(mutex_)> lock(mutex_);
44 cancel(lock, true);
45 }
46
51 boost::asio::io_context&
53 {
54 return ios_;
55 }
56
57 [[nodiscard]] boost::asio::io_context const&
59 {
60 return ios_;
61 }
62
63
69 void
71 {
72 std::unique_lock<decltype(mutex_)> lock(mutex_);
73 cancel(lock, true);
74 }
75
76 void
78 {
79 std::unique_lock<decltype(mutex_)> lock(mutex_);
80 cancel(lock, false);
81 }
82
83
89 template <class Handler>
90 void
91 sampleOne(Handler&& handler)
92 {
93 std::scoped_lock const lock(mutex_);
94 if (cancel_)
95 throw std::logic_error("IOLatencyProbe is canceled");
96 boost::asio::post(
97 ios_, SampleOp<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
98 }
99
105 template <class Handler>
106 void
107 sample(Handler&& handler)
108 {
109 std::scoped_lock const lock(mutex_);
110 if (cancel_)
111 throw std::logic_error("IOLatencyProbe is canceled");
112 boost::asio::post(
113 ios_, SampleOp<Handler>(std::forward<Handler>(handler), Clock::now(), true, this));
114 }
115
116private:
117 void
118 cancel(std::unique_lock<decltype(mutex_)>& lock, bool wait)
119 {
120 if (!cancel_)
121 {
122 --count_;
123 cancel_ = true;
124 }
125
126 if (wait)
127 cond_.wait(lock, [this] { return this->count_ == 0; });
128 }
129
130 void
132 {
133 std::scoped_lock const lock(mutex_);
134 ++count_;
135 }
136
137 void
139 {
140 std::scoped_lock const lock(mutex_);
141 if (--count_ == 0)
142 cond_.notify_all();
143 }
144
145 template <class Handler>
146 struct SampleOp
147 {
148 Handler handler;
150 bool repeat;
152
154 Handler const& handler,
155 time_point const& start,
156 bool repeat,
159 {
160 XRPL_ASSERT(
161 probe,
162 "beast::IOLatencyProbe::SampleOp::SampleOp : non-null "
163 "probe input");
164 probe->addref();
165 }
166
167 SampleOp(SampleOp&& from) noexcept
168 : handler(std::move(from.handler))
169 , start(from.start)
170 , repeat(from.repeat)
171 , probe(from.probe)
172 {
173 XRPL_ASSERT(
174 probe,
175 "beast::IOLatencyProbe::SampleOp::SampleOp(SampleOp&&) : "
176 "non-null probe input");
177 from.probe = nullptr;
178 }
179
180 SampleOp(SampleOp const&) = delete;
182 operator=(SampleOp const&) = delete;
183 SampleOp&
184 operator=(SampleOp&&) = delete;
185
187 {
188 if (probe)
189 probe->release();
190 }
191
192 void
194 {
195 if (probe == nullptr)
196 return;
197 typename Clock::time_point const now(Clock::now());
198 typename Clock::duration const elapsed(now - start);
199
200 handler(elapsed);
201
202 {
203 std::scoped_lock const lock(probe->mutex_);
204 if (probe->cancel_)
205 return;
206 }
207
208 if (repeat)
209 {
210 // Calculate when we want to sample again, and
211 // adjust for the expected latency.
212 //
213 typename Clock::time_point const when(now + probe->period_ - (2 * elapsed));
214
215 if (when <= now)
216 {
217 // The latency is too high to maintain the desired
218 // period so don't bother with a timer.
219 //
220 boost::asio::post(probe->ios_, SampleOp<Handler>(handler, now, repeat, probe));
221 }
222 else
223 {
224 probe->timer_.expires_after(when - now);
225 probe->timer_.async_wait(SampleOp<Handler>(handler, now, repeat, probe));
226 }
227 }
228 }
229
230 void
231 operator()(boost::system::error_code const& ec)
232 {
233 if (probe == nullptr)
234 return;
235 typename Clock::time_point const now(Clock::now());
236 boost::asio::post(probe->ios_, SampleOp<Handler>(handler, now, repeat, probe));
237 }
238 };
239};
240
241} // namespace beast
IOLatencyProbe(duration const &period, boost::asio::io_context &ios)
boost::asio::io_context const & getIoContext() const
std::recursive_mutex mutex_
boost::asio::io_context & getIoContext()
Return the io_context associated with the latency probe.
void sampleOne(Handler &&handler)
Measure one sample of i/o latency.
void cancel(std::unique_lock< decltype(mutex_)> &lock, bool wait)
std::condition_variable_any cond_
void cancel()
Cancel all pending i/o.
void sample(Handler &&handler)
Initiate continuous i/o latency sampling.
boost::asio::basic_waitable_timer< std::chrono::steady_clock > timer_
Clock::duration duration
boost::asio::io_context & ios_
Clock::time_point time_point
T forward(T... args)
void operator()(boost::system::error_code const &ec)
SampleOp operator=(SampleOp const &)=delete
SampleOp(SampleOp const &)=delete
SampleOp & operator=(SampleOp &&)=delete
SampleOp(SampleOp &&from) noexcept
SampleOp(Handler const &handler, time_point const &start, bool repeat, IOLatencyProbe *probe)