1use super::{TransportAddr, TransportId};
4use std::sync::{
5 Arc,
6 atomic::{AtomicUsize, Ordering::Relaxed},
7};
8use std::time::{SystemTime, UNIX_EPOCH};
9use std::vec::IntoIter;
10use tokio::sync::mpsc::{
11 Sender, UnboundedReceiver, UnboundedSender,
12 error::{TryRecvError, TrySendError},
13};
14
15#[derive(Clone, Debug)]
17pub struct ReceivedPacket {
18 pub transport_id: TransportId,
20 pub remote_addr: TransportAddr,
22 pub data: Vec<u8>,
24 pub timestamp_ms: u64,
26 #[doc(hidden)]
28 pub trace_enqueued_at: Option<crate::perf_profile::TraceStamp>,
29 #[doc(hidden)]
33 pub trace_rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
34}
35
36impl ReceivedPacket {
37 pub fn new(transport_id: TransportId, remote_addr: TransportAddr, data: Vec<u8>) -> Self {
39 Self::with_trace_timestamp(
40 transport_id,
41 remote_addr,
42 data,
43 received_timestamp_ms(),
44 crate::perf_profile::stamp(),
45 )
46 }
47
48 pub fn with_timestamp(
50 transport_id: TransportId,
51 remote_addr: TransportAddr,
52 data: Vec<u8>,
53 timestamp_ms: u64,
54 ) -> Self {
55 Self::with_trace_timestamp(
56 transport_id,
57 remote_addr,
58 data,
59 timestamp_ms,
60 crate::perf_profile::stamp(),
61 )
62 }
63
64 pub(crate) fn with_trace_timestamp(
71 transport_id: TransportId,
72 remote_addr: TransportAddr,
73 data: Vec<u8>,
74 timestamp_ms: u64,
75 trace_enqueued_at: Option<crate::perf_profile::TraceStamp>,
76 ) -> Self {
77 Self {
78 transport_id,
79 remote_addr,
80 data,
81 timestamp_ms,
82 trace_enqueued_at,
83 trace_rx_loop_owned_at: None,
84 }
85 }
86
87 pub(crate) fn is_priority_sized(&self) -> bool {
88 self.data.len() <= PRIORITY_PACKET_MAX_LEN
89 }
90}
91
92pub(crate) fn received_timestamp_ms() -> u64 {
93 SystemTime::now()
94 .duration_since(UNIX_EPOCH)
95 .map(|d| d.as_millis() as u64)
96 .unwrap_or(0)
97}
98
99const PRIORITY_PACKET_MAX_LEN: usize = 512;
103
104const TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER: usize = 4096;
111
112#[derive(Clone, Debug)]
120pub struct PacketTx {
121 priority: UnboundedSender<PacketQueueItem>,
122 bulk: Sender<PacketQueueItem>,
123 priority_queued_packets: Arc<AtomicUsize>,
126 queued_packets: Arc<AtomicUsize>,
127 bulk_queued_packets: Arc<AtomicUsize>,
128 bulk_packet_capacity: usize,
129 track_backlog: bool,
130}
131
132pub struct PacketRx {
134 priority: UnboundedReceiver<PacketQueueItem>,
135 bulk: tokio::sync::mpsc::Receiver<PacketQueueItem>,
136 priority_queued_packets: Arc<AtomicUsize>,
137 queued_packets: Arc<AtomicUsize>,
138 bulk_queued_packets: Arc<AtomicUsize>,
139 track_backlog: bool,
140 pending_priority: Option<PendingPackets>,
141 pending_bulk: Option<PendingPackets>,
142 priority_closed: bool,
143 bulk_closed: bool,
144}
145
146#[derive(Debug)]
147enum PacketQueueItem {
148 One(ReceivedPacket),
149 #[cfg_attr(not(any(target_os = "linux", target_os = "macos")), allow(dead_code))]
150 Batch(Vec<ReceivedPacket>),
151}
152
153#[derive(Clone, Copy)]
154enum PacketLane {
155 Priority,
156 Bulk,
157}
158
159#[derive(Clone, Copy)]
160enum PacketQueueTx {
161 Priority,
162 Bulk,
163}
164
165enum PacketSendFailure {
166 Closed(PacketQueueItem),
167 DroppedBulk(usize),
168}
169
170struct PendingPackets {
171 packets: IntoIter<ReceivedPacket>,
172 rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
173}
174
175#[derive(Debug, PartialEq, Eq)]
176struct PacketQueueDequeueCounts {
177 total: usize,
178 priority: usize,
179 bulk: usize,
180}
181
182impl PacketQueueTx {
183 fn try_send(self, owner: &PacketTx, item: PacketQueueItem) -> Result<(), PacketSendFailure> {
184 match self {
185 PacketQueueTx::Priority => owner
186 .priority
187 .send(item)
188 .map_err(|error| PacketSendFailure::Closed(error.0)),
189 PacketQueueTx::Bulk => {
190 let packet_count = item.packet_count();
191 match owner.bulk.try_send(item) {
192 Ok(()) => Ok(()),
193 Err(TrySendError::Full(_item)) => {
194 Err(PacketSendFailure::DroppedBulk(packet_count))
195 }
196 Err(TrySendError::Closed(item)) => Err(PacketSendFailure::Closed(item)),
197 }
198 }
199 }
200 }
201}
202
203impl PacketQueueItem {
204 fn packet_count(&self) -> usize {
205 match self {
206 PacketQueueItem::One(_) => 1,
207 PacketQueueItem::Batch(packets) => packets.len(),
208 }
209 }
210
211 fn dequeue_counts(&self, lane: PacketLane) -> PacketQueueDequeueCounts {
212 let total = self.packet_count();
213 match lane {
214 PacketLane::Priority => PacketQueueDequeueCounts {
215 total,
216 priority: total,
217 bulk: 0,
218 },
219 PacketLane::Bulk => PacketQueueDequeueCounts {
220 total,
221 priority: 0,
222 bulk: total,
223 },
224 }
225 }
226
227 fn queued_at(&self) -> Option<crate::perf_profile::TraceStamp> {
228 match self {
229 PacketQueueItem::One(packet) => packet.trace_enqueued_at,
230 PacketQueueItem::Batch(packets) => {
231 packets.first().and_then(|packet| packet.trace_enqueued_at)
232 }
233 }
234 }
235
236 fn record_dequeue_wait(&self, lane: PacketLane) {
237 let queued_at = self.queued_at();
238 if queued_at.is_none() {
239 return;
240 }
241 let counts = self.dequeue_counts(lane);
242 crate::perf_profile::record_since_split_count(
243 crate::perf_profile::Stage::TransportChannelWait,
244 crate::perf_profile::Stage::TransportPriorityChannelWait,
245 crate::perf_profile::Stage::TransportBulkChannelWait,
246 queued_at,
247 counts.total as u64,
248 counts.priority as u64,
249 counts.bulk as u64,
250 );
251 }
252}
253
254impl PendingPackets {
255 fn new(
256 packets: IntoIter<ReceivedPacket>,
257 rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
258 ) -> Self {
259 Self {
260 packets,
261 rx_loop_owned_at,
262 }
263 }
264
265 fn next(&mut self) -> Option<ReceivedPacket> {
266 let mut packet = self.packets.next()?;
267 if let Some(rx_loop_owned_at) = self.rx_loop_owned_at {
268 packet.trace_rx_loop_owned_at = Some(rx_loop_owned_at);
269 }
270 Some(packet)
271 }
272
273 fn len(&self) -> usize {
274 self.packets.len()
275 }
276}
277
278impl PacketTx {
279 pub fn send(
280 &self,
281 packet: ReceivedPacket,
282 ) -> Result<(), tokio::sync::mpsc::error::SendError<ReceivedPacket>> {
283 let tx = if packet.data.len() <= PRIORITY_PACKET_MAX_LEN {
284 PacketQueueTx::Priority
285 } else {
286 PacketQueueTx::Bulk
287 };
288 self.send_item(tx, PacketQueueItem::One(packet))
289 .map_err(|item| match item {
290 PacketQueueItem::One(packet) => tokio::sync::mpsc::error::SendError(packet),
291 PacketQueueItem::Batch(_) => {
292 unreachable!("single packet send cannot fail with a batch item")
293 }
294 })
295 }
296
297 #[cfg_attr(not(any(target_os = "linux", target_os = "macos")), allow(dead_code))]
298 pub(crate) fn send_batch(&self, packets: Vec<ReceivedPacket>) -> Result<(), ()> {
299 if packets.is_empty() {
300 return Ok(());
301 }
302
303 let packet_count = packets.len();
304 let priority_count = packets
305 .iter()
306 .filter(|packet| packet.is_priority_sized())
307 .count();
308 if priority_count == 0 || priority_count == packet_count {
309 let tx = if priority_count == 0 {
310 PacketQueueTx::Bulk
311 } else {
312 PacketQueueTx::Priority
313 };
314 return self.send_packet_items(tx, packets);
315 }
316
317 let mut priority_packets = Vec::with_capacity(priority_count);
318 let mut bulk_packets = Vec::with_capacity(packet_count - priority_count);
319 for packet in packets {
320 if packet.is_priority_sized() {
321 priority_packets.push(packet);
322 } else {
323 bulk_packets.push(packet);
324 }
325 }
326
327 self.send_packet_items(PacketQueueTx::Priority, priority_packets)?;
328 self.send_packet_items(PacketQueueTx::Bulk, bulk_packets)?;
329 Ok(())
330 }
331
332 #[cfg_attr(not(any(target_os = "linux", target_os = "macos")), allow(dead_code))]
333 fn send_packet_items(
334 &self,
335 tx: PacketQueueTx,
336 mut packets: Vec<ReceivedPacket>,
337 ) -> Result<(), ()> {
338 let item = match packets.len() {
339 0 => return Ok(()),
340 1 => PacketQueueItem::One(packets.pop().expect("one packet should be present")),
341 _ => PacketQueueItem::Batch(packets),
342 };
343 self.send_item(tx, item).map_err(|_| ())
344 }
345
346 fn send_item(&self, tx: PacketQueueTx, item: PacketQueueItem) -> Result<(), PacketQueueItem> {
347 let packet_count = item.packet_count();
348 let bulk_reserved = matches!(tx, PacketQueueTx::Bulk)
349 .then_some(packet_count)
350 .filter(|count| *count > 0);
351 let priority_reserved = matches!(tx, PacketQueueTx::Priority)
352 .then_some(packet_count)
353 .filter(|count| *count > 0);
354 if let Some(count) = priority_reserved {
355 self.priority_queued_packets.fetch_add(count, Relaxed);
356 }
357 if let Some(count) = bulk_reserved
358 && !self.try_reserve_bulk_packets(count)
359 {
360 crate::perf_profile::record_event_count(
361 crate::perf_profile::Event::TransportBulkDropped,
362 count as u64,
363 );
364 return Ok(());
365 }
366
367 let tracked_count = if self.track_backlog {
368 Some(packet_count)
369 } else {
370 None
371 };
372 let previous = tracked_count.map(|count| self.queued_packets.fetch_add(count, Relaxed));
373 match tx.try_send(self, item) {
374 Ok(()) => {
375 if let (Some(count), Some(previous)) = (tracked_count, previous) {
376 let queued = previous.saturating_add(count);
377 if previous < TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER
378 && queued >= TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER
379 {
380 crate::perf_profile::record_event(
381 crate::perf_profile::Event::TransportChannelBacklogHigh,
382 );
383 }
384 }
385 Ok(())
386 }
387 Err(PacketSendFailure::Closed(item)) => {
388 if let Some(count) = tracked_count {
389 self.queued_packets.fetch_sub(count, Relaxed);
390 }
391 if let Some(count) = priority_reserved {
392 release_priority_packets(&self.priority_queued_packets, count);
393 }
394 if let Some(count) = bulk_reserved {
395 self.release_bulk_packets(count);
396 }
397 Err(item)
398 }
399 Err(PacketSendFailure::DroppedBulk(dropped_count)) => {
400 if let Some(count) = tracked_count {
401 self.queued_packets.fetch_sub(count, Relaxed);
402 }
403 if let Some(count) = priority_reserved {
404 release_priority_packets(&self.priority_queued_packets, count);
405 }
406 if let Some(count) = bulk_reserved {
407 self.release_bulk_packets(count);
408 }
409 crate::perf_profile::record_event_count(
410 crate::perf_profile::Event::TransportBulkDropped,
411 dropped_count as u64,
412 );
413 Ok(())
414 }
415 }
416 }
417
418 fn try_reserve_bulk_packets(&self, count: usize) -> bool {
419 self.bulk_queued_packets
420 .fetch_update(Relaxed, Relaxed, |current| {
421 current
422 .checked_add(count)
423 .filter(|next| *next <= self.bulk_packet_capacity)
424 })
425 .is_ok()
426 }
427
428 fn release_bulk_packets(&self, count: usize) {
429 release_reserved_bulk_packets(&self.bulk_queued_packets, count);
430 }
431
432 #[cfg(test)]
433 pub(crate) fn queued_packets(&self) -> usize {
434 self.queued_packets.load(Relaxed)
435 }
436
437 #[cfg(test)]
438 pub(crate) fn priority_queued_packets(&self) -> usize {
439 self.priority_queued_packets.load(Relaxed)
440 }
441
442 #[cfg(test)]
443 pub(crate) fn bulk_queued_packets(&self) -> usize {
444 self.bulk_queued_packets.load(Relaxed)
445 }
446}
447
448impl PacketRx {
449 pub(crate) fn priority_queued_packets(&self) -> usize {
450 self.priority_queued_packets.load(Relaxed)
451 }
452
453 pub(crate) fn ready_packets(&self) -> usize {
454 self.pending_priority
455 .as_ref()
456 .map_or(0, PendingPackets::len)
457 .saturating_add(self.pending_bulk.as_ref().map_or(0, PendingPackets::len))
458 .saturating_add(self.priority_queued_packets())
459 .saturating_add(self.bulk_queued_packets.load(Relaxed))
460 }
461
462 pub(crate) fn priority_ready_packets(&self) -> usize {
463 self.pending_priority
464 .as_ref()
465 .map_or(0, PendingPackets::len)
466 .saturating_add(self.priority_queued_packets())
467 }
468
469 pub async fn recv(&mut self) -> Option<ReceivedPacket> {
470 loop {
471 match self.try_recv() {
472 Ok(packet) => return Some(packet),
473 Err(TryRecvError::Disconnected) => return None,
474 Err(TryRecvError::Empty) => {}
475 }
476
477 tokio::select! {
478 biased;
479 item = self.priority.recv(), if !self.priority_closed => {
480 match item {
481 Some(item) => {
482 if let Some(packet) = self.packet_from_item(item, PacketLane::Priority) {
483 return Some(packet);
484 }
485 }
486 None => self.priority_closed = true,
487 }
488 }
489 item = self.bulk.recv(), if !self.bulk_closed => {
490 match item {
491 Some(item) => {
492 if let Some(packet) = self.packet_from_item(item, PacketLane::Bulk) {
493 return Some(packet);
494 }
495 }
496 None => self.bulk_closed = true,
497 }
498 }
499 }
500 }
501 }
502
503 pub fn try_recv(&mut self) -> Result<ReceivedPacket, TryRecvError> {
504 if let Some(packet) = Self::take_pending(&mut self.pending_priority) {
505 return Ok(packet);
506 }
507
508 if self.should_probe_priority() {
509 match self.priority.try_recv() {
510 Ok(item) => {
511 if let Some(packet) = self.packet_from_item(item, PacketLane::Priority) {
512 return Ok(packet);
513 }
514 }
515 Err(TryRecvError::Empty) => {}
516 Err(TryRecvError::Disconnected) => {
517 self.priority_closed = true;
518 }
519 }
520 }
521
522 if let Some(packet) = Self::take_pending(&mut self.pending_bulk) {
523 return Ok(packet);
524 }
525
526 match self.bulk.try_recv() {
527 Ok(item) => self
528 .packet_from_item(item, PacketLane::Bulk)
529 .ok_or(TryRecvError::Empty),
530 Err(TryRecvError::Empty) => {
531 if self.priority_closed && self.bulk_closed {
532 Err(TryRecvError::Disconnected)
533 } else {
534 Err(TryRecvError::Empty)
535 }
536 }
537 Err(TryRecvError::Disconnected) => {
538 self.bulk_closed = true;
539 if self.priority_closed {
540 Err(TryRecvError::Disconnected)
541 } else {
542 Err(TryRecvError::Empty)
543 }
544 }
545 }
546 }
547
548 fn packet_from_item(
549 &mut self,
550 item: PacketQueueItem,
551 lane: PacketLane,
552 ) -> Option<ReceivedPacket> {
553 item.record_dequeue_wait(lane);
554 let packet_count = item.packet_count();
555 if self.track_backlog {
556 self.queued_packets.fetch_sub(packet_count, Relaxed);
557 }
558 if matches!(lane, PacketLane::Priority) {
559 release_priority_packets(&self.priority_queued_packets, packet_count);
560 }
561 if matches!(lane, PacketLane::Bulk) {
562 release_reserved_bulk_packets(&self.bulk_queued_packets, packet_count);
563 }
564 let rx_loop_owned_at = crate::perf_profile::stamp();
565 match item {
566 PacketQueueItem::One(mut packet) => {
567 packet.trace_rx_loop_owned_at = rx_loop_owned_at;
568 Some(packet)
569 }
570 PacketQueueItem::Batch(packets) => {
571 let mut packets = packets.into_iter();
572 let mut packet = packets.next()?;
573 if let Some(rx_loop_owned_at) = rx_loop_owned_at {
574 packet.trace_rx_loop_owned_at = Some(rx_loop_owned_at);
575 }
576 if packets.len() > 0 {
577 let pending = PendingPackets::new(packets, rx_loop_owned_at);
578 match lane {
579 PacketLane::Priority => self.pending_priority = Some(pending),
580 PacketLane::Bulk => self.pending_bulk = Some(pending),
581 }
582 }
583 Some(packet)
584 }
585 }
586 }
587
588 fn should_probe_priority(&self) -> bool {
589 !self.priority_closed
590 && (self.priority_queued_packets.load(Relaxed) > 0 || self.bulk_closed)
591 }
592
593 fn take_pending(pending: &mut Option<PendingPackets>) -> Option<ReceivedPacket> {
594 let packets = pending.as_mut()?;
595 let packet = packets.next();
596 if packets.len() == 0 {
597 *pending = None;
598 }
599 packet
600 }
601}
602
603#[inline]
604fn packet_channel_tracks_backlog() -> bool {
605 cfg!(test) || crate::perf_profile::enabled()
606}
607
608fn release_reserved_bulk_packets(counter: &AtomicUsize, count: usize) {
609 if count == 0 {
610 return;
611 }
612
613 let previous = counter.fetch_sub(count, Relaxed);
614 debug_assert!(
615 previous >= count,
616 "transport bulk queued packet accounting underflow"
617 );
618}
619
620fn release_priority_packets(counter: &AtomicUsize, count: usize) {
621 if count == 0 {
622 return;
623 }
624
625 let previous = counter.fetch_sub(count, Relaxed);
626 debug_assert!(
627 previous >= count,
628 "transport priority queued packet accounting underflow"
629 );
630}
631
632pub fn packet_channel(buffer: usize) -> (PacketTx, PacketRx) {
638 let (priority_tx, priority_rx) = tokio::sync::mpsc::unbounded_channel();
639 let (bulk_tx, bulk_rx) = tokio::sync::mpsc::channel(buffer.max(1));
640 let priority_queued_packets = Arc::new(AtomicUsize::new(0));
641 let queued_packets = Arc::new(AtomicUsize::new(0));
642 let bulk_queued_packets = Arc::new(AtomicUsize::new(0));
643 let track_backlog = packet_channel_tracks_backlog();
644 (
645 PacketTx {
646 priority: priority_tx,
647 bulk: bulk_tx,
648 priority_queued_packets: Arc::clone(&priority_queued_packets),
649 queued_packets: Arc::clone(&queued_packets),
650 bulk_queued_packets: Arc::clone(&bulk_queued_packets),
651 bulk_packet_capacity: buffer.max(1),
652 track_backlog,
653 },
654 PacketRx {
655 priority: priority_rx,
656 bulk: bulk_rx,
657 priority_queued_packets,
658 queued_packets,
659 bulk_queued_packets,
660 track_backlog,
661 pending_priority: None,
662 pending_bulk: None,
663 priority_closed: false,
664 bulk_closed: false,
665 },
666 )
667}
668
669#[cfg(test)]
670mod tests;