27#if defined(__i386__) || defined(__x86_64__)
29#elif defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__))
32 std::atomic_signal_fence(std::memory_order_seq_cst);
38 _sender(std::
move(
sender)), _queue_depth(queue_depth)
42 throw std::invalid_argument(
"PacedSender requires a datagram sender");
46 throw std::invalid_argument(
"PacedSender queue depth must be positive");
52 void PacedSender::open(
const std::string& host,
const std::uint16_t port) { _sender->open(host, port); }
56 std::scoped_lock
const lock(_mutex);
62 _steady_epoch = std::chrono::steady_clock::now();
65 _thread = std::thread([
this] { run(); });
70 std::unique_lock
lock(_mutex);
73 throw std::logic_error(
"PacedSender must be started before enqueue");
79 .dropped = std::nullopt,
83 while (queuedOrSendingCount() >= _queue_depth)
85 const bool precedes_latest = !_queue.empty() &&
packet.first_sample_time < _queue.back().first_sample_time;
88 _priority_overflow_in_progress =
true;
89 insertByDeadlineUnlocked(std::move(
packet));
91 _cv.wait(
lock, [
this] {
return _stopping || queuedOrSendingCount() <= _queue_depth; });
92 _priority_overflow_in_progress =
false;
102 .dropped = std::nullopt,
107 insertByDeadlineUnlocked(std::move(
packet));
119 std::unique_lock
lock(_mutex);
122 throw std::logic_error(
"PacedSender must be started before enqueue");
129 for (
auto&
packet : packets)
131 insertByDeadlineUnlocked(std::move(
packet));
134 _cv.wait(
lock, [
this] {
return _stopping || queuedOrSendingCount() <= _queue_depth; });
140 std::unique_lock
lock(_mutex);
141 _cv.wait(
lock, [
this] {
return _queue.empty() && !_send_in_progress; });
147 std::scoped_lock
const lock(_mutex);
148 if (!_started && !_thread.joinable())
157 if (_thread.joinable())
163 std::scoped_lock
const lock(_mutex);
166 _send_in_progress =
false;
174 std::scoped_lock
const lock(_mutex);
175 const auto found = _late_data_packets.find(stream_id);
176 return found == _late_data_packets.end() ? 0 :
found->second;
181 std::scoped_lock
const lock(_mutex);
182 const auto found = _late_context_packets.find(stream_id);
183 return found == _late_context_packets.end() ? 0 :
found->second;
188 std::scoped_lock
const lock(_mutex);
189 const auto found = _sent_packets.find(stream_id);
190 return found == _sent_packets.end() ? 0 :
found->second;
195 std::scoped_lock
const lock(_mutex);
196 const auto found = _send_failures.find(stream_id);
197 return found == _send_failures.end() ? 0 :
found->second;
202 std::scoped_lock
const lock(_mutex);
203 const auto found = _dropped_data_packets.find(stream_id);
204 return found == _dropped_data_packets.end() ? 0 :
found->second;
209 std::scoped_lock
const lock(_mutex);
210 const auto found = _dropped_context_packets.find(stream_id);
211 return found == _dropped_context_packets.end() ? 0 :
found->second;
216 std::scoped_lock
const lock(_mutex);
217 const auto found = _dropped_samples.find(stream_id);
218 return found == _dropped_samples.end() ? 0 :
found->second;
223 std::scoped_lock
const lock(_mutex);
224 auto result = std::move(_pending_dropped_datagrams);
225 _pending_dropped_datagrams.clear();
229 void PacedSender::run()
231 std::unique_lock
lock(_mutex);
240 _cv.wait(
lock, [
this] {
return _stopping || !_queue.empty(); });
244 const auto due = dueTime(_queue.front());
245 if (std::chrono::steady_clock::now() <
due)
251 auto packet = std::move(_queue.front());
253 _send_in_progress =
true;
254 const auto now = std::chrono::steady_clock::now();
258 _send_in_progress =
false;
263 void PacedSender::waitUntilDue(std::unique_lock<std::mutex>&
lock,
const std::chrono::steady_clock::time_point
due)
265 const auto now = std::chrono::steady_clock::now();
283 while (std::chrono::steady_clock::now() <
due)
290 void PacedSender::insertByDeadlineUnlocked(SerializedPacket
packet)
292 const auto position = std::upper_bound(_queue.begin(), _queue.end(),
packet.first_sample_time,
294 { return deadline < queued.first_sample_time; });
298 void PacedSender::sendOneUnlocked(SerializedPacket
packet,
const std::chrono::steady_clock::time_point
now)
303 _sender->send(
packet.bytes);
307 std::scoped_lock
const lock(_mutex);
308 ++_send_failures[
packet.stream_id];
309 recordDroppedUnlocked(
packet);
310 _pending_dropped_datagrams.push_back(makeDroppedDatagram(
packet));
313 std::scoped_lock
const lock(_mutex);
314 ++_sent_packets[
packet.stream_id];
315 if (
now >
due + std::chrono::milliseconds(1))
317 if (
packet.context_packet)
319 ++_late_context_packets[
packet.stream_id];
323 ++_late_data_packets[
packet.stream_id];
328 void PacedSender::recordDroppedUnlocked(
const SerializedPacket&
packet)
332 ++_dropped_data_packets[
packet.stream_id];
333 _dropped_samples[
packet.stream_id] +=
packet.sample_count;
336 if (
packet.context_packet)
338 ++_dropped_context_packets[
packet.stream_id];
342 DroppedDatagram PacedSender::makeDroppedDatagram(
const SerializedPacket&
packet)
const noexcept
344 return DroppedDatagram{.stream_id =
packet.stream_id,
345 .sample_count =
packet.sample_count,
346 .data_packet =
packet.data_packet,
347 .context_packet =
packet.context_packet};
350 std::size_t PacedSender::queuedOrSendingCount()
const noexcept
352 return _queue.size() + (_send_in_progress ? 1u : 0
u);
355 std::chrono::steady_clock::time_point PacedSender::dueTime(
const SerializedPacket&
packet)
const
357 const auto seconds =
packet.first_sample_time - _simulation_epoch_time;
358 const auto nanos =
static_cast<std::int64_t
>(
seconds * 1'000'000'000.0);
359 return _steady_epoch + std::chrono::nanoseconds(
nanos);