1use super::{TransportAddr, TransportId};
4use std::mem;
5use std::sync::{
6 Arc, Mutex,
7 atomic::{AtomicUsize, Ordering::Relaxed},
8};
9use tokio::sync::mpsc::{
10 Sender, UnboundedReceiver, UnboundedSender,
11 error::{TryRecvError, TrySendError},
12};
13
14pub(crate) trait PacketFastIngressSink: std::fmt::Debug + Send + Sync {
15 fn try_ingest_batch(&self, packets: &mut Vec<ReceivedPacket>) -> usize;
16}
17
18#[derive(Clone, Debug)]
20pub struct ReceivedPacket {
21 pub transport_id: TransportId,
23 pub remote_addr: TransportAddr,
25 pub data: PacketBuffer,
27 pub timestamp_ms: u64,
29 #[doc(hidden)]
31 pub trace_enqueued_at: Option<crate::perf_profile::TraceStamp>,
32 #[doc(hidden)]
36 pub trace_rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
37}
38
39impl ReceivedPacket {
40 pub fn with_timestamp(
42 transport_id: TransportId,
43 remote_addr: TransportAddr,
44 data: PacketBuffer,
45 timestamp_ms: u64,
46 ) -> Self {
47 Self::with_trace_timestamp(
48 transport_id,
49 remote_addr,
50 data,
51 timestamp_ms,
52 crate::perf_profile::stamp(),
53 )
54 }
55
56 pub(crate) fn with_trace_timestamp(
63 transport_id: TransportId,
64 remote_addr: TransportAddr,
65 data: PacketBuffer,
66 timestamp_ms: u64,
67 trace_enqueued_at: Option<crate::perf_profile::TraceStamp>,
68 ) -> Self {
69 Self {
70 transport_id,
71 remote_addr,
72 data,
73 timestamp_ms,
74 trace_enqueued_at,
75 trace_rx_loop_owned_at: None,
76 }
77 }
78
79 pub(crate) fn is_transport_priority(&self) -> bool {
80 is_transport_priority_packet(self.data.as_slice())
81 }
82}
83
84#[derive(Debug, Default)]
89pub struct PacketBuffer {
90 data: Vec<u8>,
91 start: usize,
92 pool: Option<PacketBufferPool>,
93}
94
95impl PacketBuffer {
96 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
97 fn pooled(data: Vec<u8>, pool: PacketBufferPool) -> Self {
98 Self {
99 data,
100 start: 0,
101 pool: Some(pool),
102 }
103 }
104
105 pub fn new(data: Vec<u8>) -> Self {
106 Self {
107 data,
108 start: 0,
109 pool: None,
110 }
111 }
112
113 pub fn as_slice(&self) -> &[u8] {
114 &self.data[self.start..]
115 }
116
117 pub fn as_mut_slice(&mut self) -> &mut [u8] {
118 &mut self.data[self.start..]
119 }
120
121 pub fn len(&self) -> usize {
122 self.data.len().saturating_sub(self.start)
123 }
124
125 pub fn is_empty(&self) -> bool {
126 self.len() == 0
127 }
128
129 pub fn into_vec(mut self) -> Vec<u8> {
130 self.pool = None;
131 if self.start > 0 {
132 self.data.drain(..self.start);
133 self.start = 0;
134 }
135 mem::take(&mut self.data)
136 }
137
138 pub(crate) fn trim_front(&mut self, len: usize) -> bool {
139 if len > self.len() {
140 return false;
141 }
142 self.start += len;
143 true
144 }
145
146 pub(crate) fn truncate(&mut self, len: usize) {
147 if len < self.len() {
148 self.data.truncate(self.start + len);
149 }
150 }
151
152 pub(crate) fn extend_from_slice(&mut self, bytes: &[u8]) {
153 self.data.extend_from_slice(bytes);
154 }
155
156 pub(crate) fn try_prepend_slices(&mut self, parts: &[&[u8]], reserve_tail: usize) -> bool {
157 let prefix_len = parts
158 .iter()
159 .fold(0usize, |total, part| total.saturating_add(part.len()));
160 if prefix_len == 0 {
161 return self.data.capacity().saturating_sub(self.data.len()) >= reserve_tail;
162 }
163
164 let len = self.data.len();
165 if self.start >= prefix_len && self.data.capacity().saturating_sub(len) >= reserve_tail {
166 let new_start = self.start - prefix_len;
167 let mut offset = new_start;
168 for part in parts {
169 self.data[offset..offset + part.len()].copy_from_slice(part);
170 offset += part.len();
171 }
172 self.start = new_start;
173 return true;
174 }
175
176 if self.data.capacity().saturating_sub(len) < prefix_len.saturating_add(reserve_tail) {
177 return false;
178 }
179
180 unsafe {
184 let ptr = self.data.as_mut_ptr();
185 std::ptr::copy(
186 ptr.add(self.start),
187 ptr.add(self.start + prefix_len),
188 self.len(),
189 );
190 let mut offset = self.start;
191 for part in parts {
192 std::ptr::copy_nonoverlapping(part.as_ptr(), ptr.add(offset), part.len());
193 offset += part.len();
194 }
195 self.data.set_len(len + prefix_len);
196 }
197 true
198 }
199
200 pub(crate) fn replace_visible_prefix(&mut self, remove_len: usize, prefix: &[u8]) -> bool {
201 if remove_len > self.len() {
202 return false;
203 }
204
205 let prefix_len = prefix.len();
206 let tail_len = self.len() - remove_len;
207 if prefix_len >= remove_len {
208 let grow = prefix_len - remove_len;
209 if grow > 0 && self.start >= grow {
210 let new_start = self.start - grow;
211 self.data[new_start..new_start + prefix_len].copy_from_slice(prefix);
212 self.start = new_start;
213 return true;
214 }
215
216 let len = self.data.len();
217 if grow > 0 {
218 self.data.reserve(grow);
219 unsafe {
220 let ptr = self.data.as_mut_ptr();
221 std::ptr::copy(
222 ptr.add(self.start + remove_len),
223 ptr.add(self.start + prefix_len),
224 tail_len,
225 );
226 self.data.set_len(len + grow);
227 }
228 }
229 self.data[self.start..self.start + prefix_len].copy_from_slice(prefix);
230 return true;
231 }
232
233 let shrink = remove_len - prefix_len;
234 if tail_len > 0 {
235 self.data.copy_within(
236 self.start + remove_len..self.start + remove_len + tail_len,
237 self.start + prefix_len,
238 );
239 }
240 self.data.truncate(self.data.len() - shrink);
241 self.data[self.start..self.start + prefix_len].copy_from_slice(prefix);
242 true
243 }
244}
245
246impl Clone for PacketBuffer {
247 fn clone(&self) -> Self {
248 Self {
249 data: self.as_slice().to_vec(),
250 start: 0,
251 pool: None,
252 }
253 }
254}
255
256impl AsRef<[u8]> for PacketBuffer {
257 fn as_ref(&self) -> &[u8] {
258 self.as_slice()
259 }
260}
261
262impl Drop for PacketBuffer {
263 fn drop(&mut self) {
264 if let Some(pool) = self.pool.take() {
265 pool.put(mem::take(&mut self.data));
266 }
267 }
268}
269
270impl PartialEq for PacketBuffer {
271 fn eq(&self, other: &Self) -> bool {
272 self.as_slice() == other.as_slice()
273 }
274}
275
276impl Eq for PacketBuffer {}
277
278const FMP_VERSION: u8 = crate::node::wire::FMP_VERSION;
284const FMP_PHASE_ESTABLISHED: u8 = crate::node::wire::PHASE_ESTABLISHED;
285const FMP_PHASE_MSG1: u8 = crate::node::wire::PHASE_MSG1;
286const FMP_PHASE_MSG2: u8 = crate::node::wire::PHASE_MSG2;
287const FMP_COMMON_PREFIX_SIZE: usize = crate::node::wire::COMMON_PREFIX_SIZE;
288const FMP_ESTABLISHED_HEADER_SIZE: usize = crate::node::wire::ESTABLISHED_HEADER_SIZE;
289const FMP_MSG1_WIRE_SIZE: usize = crate::node::wire::MSG1_WIRE_SIZE;
290const FMP_MSG2_WIRE_SIZE: usize = crate::node::wire::MSG2_WIRE_SIZE;
291const AEAD_TAG_SIZE: usize = crate::noise::TAG_SIZE;
292const FMP_HEARTBEAT_PLAINTEXT_SIZE: usize = 4 + 1;
293const FMP_MMP_SENDER_REPORT_PLAINTEXT_SIZE: usize = crate::mmp::SENDER_REPORT_WIRE_SIZE;
294const FMP_MMP_RECEIVER_REPORT_PLAINTEXT_SIZE: usize = crate::mmp::RECEIVER_REPORT_WIRE_SIZE;
295
296fn is_transport_priority_packet(data: &[u8]) -> bool {
297 if data.len() < FMP_COMMON_PREFIX_SIZE {
298 return false;
299 }
300
301 let version = data[0] >> 4;
302 let phase = data[0] & 0x0F;
303 if version != FMP_VERSION {
304 return false;
305 }
306
307 match phase {
308 FMP_PHASE_MSG1 => data.len() == FMP_MSG1_WIRE_SIZE,
309 FMP_PHASE_MSG2 => data.len() == FMP_MSG2_WIRE_SIZE,
310 FMP_PHASE_ESTABLISHED => is_fmp_established_priority_packet(data),
311 _ => false,
312 }
313}
314
315fn is_fmp_established_priority_packet(data: &[u8]) -> bool {
316 if data.len() < FMP_ESTABLISHED_HEADER_SIZE.saturating_add(AEAD_TAG_SIZE) {
317 return false;
318 }
319
320 let payload_len = usize::from(u16::from_le_bytes([data[2], data[3]]));
321 let expected_len = FMP_ESTABLISHED_HEADER_SIZE
322 .saturating_add(payload_len)
323 .saturating_add(AEAD_TAG_SIZE);
324 if data.len() != expected_len {
325 return false;
326 }
327
328 matches!(
329 payload_len,
330 FMP_HEARTBEAT_PLAINTEXT_SIZE
331 | FMP_MMP_SENDER_REPORT_PLAINTEXT_SIZE
332 | FMP_MMP_RECEIVER_REPORT_PLAINTEXT_SIZE
333 )
334}
335
336const PACKET_BATCH_POOL_LIMIT: usize = 256;
338const PACKET_BATCH_MAX_RETAINED_CAPACITY: usize = 256;
340const PACKET_BUFFER_POOL_LIMIT: usize = 4096;
342const PACKET_BUFFER_MAX_RETAINED_CAPACITY: usize = 16 * 1024;
344
345const TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER: usize = 16_384;
352
353#[derive(Clone, Debug)]
361pub struct PacketTx {
362 priority: UnboundedSender<PacketQueueItem>,
363 bulk: Sender<PacketQueueItem>,
364 fast_ingress: Option<Arc<dyn PacketFastIngressSink>>,
365 batch_pool: PacketBatchPool,
366 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
367 buffer_pool: PacketBufferPool,
368 priority_queued_packets: Arc<AtomicUsize>,
371 queued_packets: Arc<AtomicUsize>,
372 bulk_queued_packets: Arc<AtomicUsize>,
373 bulk_packet_capacity: usize,
374 track_backlog: bool,
375}
376
377pub struct PacketRx {
379 priority: UnboundedReceiver<PacketQueueItem>,
380 bulk: tokio::sync::mpsc::Receiver<PacketQueueItem>,
381 priority_queued_packets: Arc<AtomicUsize>,
382 queued_packets: Arc<AtomicUsize>,
383 bulk_queued_packets: Arc<AtomicUsize>,
384 track_backlog: bool,
385 pending_priority: Option<PendingPackets>,
386 pending_bulk: Option<PendingPackets>,
387 priority_closed: bool,
388 bulk_closed: bool,
389}
390
391#[derive(Clone, Debug)]
392struct PacketBatchPool {
393 inner: Arc<Mutex<Vec<Vec<ReceivedPacket>>>>,
394}
395
396#[derive(Clone, Debug)]
397struct PacketBufferPool {
398 inner: Arc<Mutex<Vec<Vec<u8>>>>,
399 available: Arc<AtomicUsize>,
400}
401
402#[derive(Debug)]
403pub(crate) struct PacketBatch {
404 packets: Vec<ReceivedPacket>,
405 pool: Option<PacketBatchPool>,
406}
407
408#[derive(Debug)]
409enum PacketQueueItem {
410 One(ReceivedPacket),
411 Batch(PacketBatch),
412}
413
414#[derive(Clone, Copy)]
415enum PacketLane {
416 Priority,
417 Bulk,
418}
419
420#[derive(Clone, Copy)]
421enum PacketQueueTx {
422 Priority,
423 Bulk,
424}
425
426enum PacketSendFailure {
427 Closed(PacketQueueItem),
428 DroppedBulk(usize),
429}
430
431struct PendingPackets {
432 batch: PacketBatch,
433 rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
434}
435
436#[derive(Debug, PartialEq, Eq)]
437struct PacketQueueDequeueCounts {
438 total: usize,
439 priority: usize,
440 bulk: usize,
441}
442
443impl PacketQueueTx {
444 fn try_send(self, owner: &PacketTx, item: PacketQueueItem) -> Result<(), PacketSendFailure> {
445 match self {
446 PacketQueueTx::Priority => owner
447 .priority
448 .send(item)
449 .map_err(|error| PacketSendFailure::Closed(error.0)),
450 PacketQueueTx::Bulk => {
451 let packet_count = item.packet_count();
452 match owner.bulk.try_send(item) {
453 Ok(()) => Ok(()),
454 Err(TrySendError::Full(_item)) => {
455 Err(PacketSendFailure::DroppedBulk(packet_count))
456 }
457 Err(TrySendError::Closed(item)) => Err(PacketSendFailure::Closed(item)),
458 }
459 }
460 }
461 }
462}
463
464impl PacketQueueItem {
465 fn packet_count(&self) -> usize {
466 match self {
467 PacketQueueItem::One(_) => 1,
468 PacketQueueItem::Batch(packets) => packets.packets.len(),
469 }
470 }
471
472 fn dequeue_counts(&self, lane: PacketLane) -> PacketQueueDequeueCounts {
473 let total = self.packet_count();
474 match lane {
475 PacketLane::Priority => PacketQueueDequeueCounts {
476 total,
477 priority: total,
478 bulk: 0,
479 },
480 PacketLane::Bulk => PacketQueueDequeueCounts {
481 total,
482 priority: 0,
483 bulk: total,
484 },
485 }
486 }
487
488 fn queued_at(&self) -> Option<crate::perf_profile::TraceStamp> {
489 match self {
490 PacketQueueItem::One(packet) => packet.trace_enqueued_at,
491 PacketQueueItem::Batch(packets) => packets
492 .packets
493 .first()
494 .and_then(|packet| packet.trace_enqueued_at),
495 }
496 }
497
498 fn record_dequeue_wait(&self, lane: PacketLane) {
499 let queued_at = self.queued_at();
500 if queued_at.is_none() {
501 return;
502 }
503 let counts = self.dequeue_counts(lane);
504 crate::perf_profile::record_since_split_count(
505 crate::perf_profile::Stage::TransportChannelWait,
506 crate::perf_profile::Stage::TransportPriorityChannelWait,
507 crate::perf_profile::Stage::TransportBulkChannelWait,
508 queued_at,
509 counts.total as u64,
510 counts.priority as u64,
511 counts.bulk as u64,
512 );
513 }
514}
515
516impl PacketBatchPool {
517 fn new() -> Self {
518 Self {
519 inner: Arc::new(Mutex::new(Vec::new())),
520 }
521 }
522
523 fn take(&self, capacity: usize) -> PacketBatch {
524 let packets = {
525 let mut guard = self.inner.lock().unwrap_or_else(|error| error.into_inner());
526 guard.pop()
527 };
528 if let Some(mut packets) = packets {
529 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBatchPoolReuse);
530 packets.clear();
531 if packets.capacity() >= capacity {
532 return PacketBatch::pooled(packets, self.clone());
533 }
534 packets.reserve(capacity.saturating_sub(packets.capacity()));
535 return PacketBatch::pooled(packets, self.clone());
536 }
537 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBatchPoolFresh);
538 PacketBatch::pooled(Vec::with_capacity(capacity), self.clone())
539 }
540
541 fn put(&self, mut packets: Vec<ReceivedPacket>) {
542 packets.clear();
543 if packets.capacity() > PACKET_BATCH_MAX_RETAINED_CAPACITY {
544 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBatchPoolDiscard);
545 return;
546 }
547 let mut guard = self.inner.lock().unwrap_or_else(|error| error.into_inner());
548 if guard.len() < PACKET_BATCH_POOL_LIMIT {
549 guard.push(packets);
550 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBatchPoolReturn);
551 } else {
552 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBatchPoolDiscard);
553 }
554 }
555}
556
557impl PacketBufferPool {
558 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
559 fn new() -> Self {
560 Self {
561 inner: Arc::new(Mutex::new(Vec::new())),
562 available: Arc::new(AtomicUsize::new(0)),
563 }
564 }
565
566 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
567 fn take(&self, capacity: usize) -> Vec<u8> {
568 if self.available.load(Relaxed) > 0 {
569 let buffer = {
570 let mut guard = self.inner.lock().unwrap_or_else(|error| error.into_inner());
571 guard.pop()
572 };
573 if let Some(mut buffer) = buffer {
574 self.available.fetch_sub(1, Relaxed);
575 crate::perf_profile::record_event(
576 crate::perf_profile::Event::PacketBufferPoolReuse,
577 );
578 prepare_recv_buffer(&mut buffer, capacity);
579 return buffer;
580 }
581 }
582
583 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBufferPoolFresh);
584 fresh_recv_buffer(capacity)
585 }
586
587 fn put(&self, mut buffer: Vec<u8>) {
588 buffer.clear();
589 if buffer.capacity() > PACKET_BUFFER_MAX_RETAINED_CAPACITY {
590 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBufferPoolDiscard);
591 return;
592 }
593
594 let mut guard = self.inner.lock().unwrap_or_else(|error| error.into_inner());
595 if guard.len() < PACKET_BUFFER_POOL_LIMIT {
596 guard.push(buffer);
597 self.available.fetch_add(1, Relaxed);
598 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBufferPoolReturn);
599 } else {
600 crate::perf_profile::record_event(crate::perf_profile::Event::PacketBufferPoolDiscard);
601 }
602 }
603}
604
605#[cfg(target_os = "macos")]
606fn fresh_recv_buffer(size: usize) -> Vec<u8> {
607 vec![0u8; size]
608}
609
610#[cfg(all(any(test, target_os = "linux"), not(target_os = "macos")))]
611fn fresh_recv_buffer(size: usize) -> Vec<u8> {
612 Vec::with_capacity(size)
613}
614
615#[cfg(target_os = "macos")]
616fn prepare_recv_buffer(buffer: &mut Vec<u8>, size: usize) {
617 buffer.resize(size, 0);
618}
619
620#[cfg(all(any(test, target_os = "linux"), not(target_os = "macos")))]
621fn prepare_recv_buffer(buffer: &mut Vec<u8>, size: usize) {
622 buffer.clear();
623 if buffer.capacity() < size {
624 buffer.reserve(size.saturating_sub(buffer.capacity()));
625 }
626}
627
628impl PacketBatch {
629 fn pooled(packets: Vec<ReceivedPacket>, pool: PacketBatchPool) -> Self {
630 Self {
631 packets,
632 pool: Some(pool),
633 }
634 }
635
636 pub(crate) fn push(&mut self, packet: ReceivedPacket) {
637 self.packets.push(packet);
638 }
639
640 pub(crate) fn is_empty(&self) -> bool {
641 self.packets.is_empty()
642 }
643
644 pub(crate) fn as_slice(&self) -> &[ReceivedPacket] {
645 &self.packets
646 }
647}
648
649impl Drop for PacketBatch {
650 fn drop(&mut self) {
651 let Some(pool) = self.pool.take() else {
652 return;
653 };
654 pool.put(mem::take(&mut self.packets));
655 }
656}
657
658impl PendingPackets {
659 fn new(
660 mut batch: PacketBatch,
661 rx_loop_owned_at: Option<crate::perf_profile::TraceStamp>,
662 ) -> Self {
663 batch.packets.reverse();
664 Self {
665 batch,
666 rx_loop_owned_at,
667 }
668 }
669
670 fn next(&mut self) -> Option<ReceivedPacket> {
671 let mut packet = self.batch.packets.pop()?;
672 if let Some(rx_loop_owned_at) = self.rx_loop_owned_at {
673 packet.trace_rx_loop_owned_at = Some(rx_loop_owned_at);
674 }
675 Some(packet)
676 }
677}
678
679impl PacketTx {
680 pub(crate) fn set_fast_ingress_sink(&mut self, sink: Arc<dyn PacketFastIngressSink>) {
681 self.fast_ingress = Some(sink);
682 }
683
684 pub(crate) fn try_fast_ingress_packet_batch(&self, batch: &mut PacketBatch) -> usize {
685 let Some(sink) = &self.fast_ingress else {
686 return 0;
687 };
688 sink.try_ingest_batch(&mut batch.packets)
689 }
690
691 pub(crate) fn packet_batch(&self, capacity: usize) -> PacketBatch {
692 self.batch_pool.take(capacity)
693 }
694
695 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
696 pub(crate) fn recv_buffer(&self, capacity: usize) -> Vec<u8> {
697 self.buffer_pool.take(capacity)
698 }
699
700 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
701 pub(crate) fn packet_buffer(&self, data: Vec<u8>) -> PacketBuffer {
702 PacketBuffer::pooled(data, self.buffer_pool.clone())
703 }
704
705 pub fn send(
706 &self,
707 packet: ReceivedPacket,
708 ) -> Result<(), tokio::sync::mpsc::error::SendError<ReceivedPacket>> {
709 let tx = if packet.is_transport_priority() {
710 PacketQueueTx::Priority
711 } else {
712 PacketQueueTx::Bulk
713 };
714 self.send_item(tx, PacketQueueItem::One(packet))
715 .map_err(|item| match item {
716 PacketQueueItem::One(packet) => tokio::sync::mpsc::error::SendError(packet),
717 PacketQueueItem::Batch(_) => {
718 unreachable!("single packet send cannot fail with a batch item")
719 }
720 })
721 }
722
723 pub(crate) fn send_packet_batch(&self, mut batch: PacketBatch) -> Result<(), ()> {
724 if batch.is_empty() {
725 return Ok(());
726 }
727
728 let packet_count = batch.packets.len();
729 let priority_count = batch
730 .packets
731 .iter()
732 .filter(|packet| packet.is_transport_priority())
733 .count();
734 if priority_count == 0 || priority_count == packet_count {
735 let tx = if priority_count == 0 {
736 PacketQueueTx::Bulk
737 } else {
738 PacketQueueTx::Priority
739 };
740 return self.send_packet_items(tx, batch);
741 }
742
743 let mut priority_packets = self.packet_batch(priority_count);
744 let mut bulk_packets = self.packet_batch(packet_count - priority_count);
745 for packet in batch.packets.drain(..) {
746 if packet.is_transport_priority() {
747 priority_packets.push(packet);
748 } else {
749 bulk_packets.push(packet);
750 }
751 }
752
753 self.send_packet_items(PacketQueueTx::Priority, priority_packets)?;
754 self.send_packet_items(PacketQueueTx::Bulk, bulk_packets)?;
755 Ok(())
756 }
757
758 fn send_packet_items(&self, tx: PacketQueueTx, packets: PacketBatch) -> Result<(), ()> {
759 if matches!(tx, PacketQueueTx::Bulk) {
760 return self.send_bulk_packet_items(packets);
761 }
762
763 let item = match packets.packets.len() {
764 0 => return Ok(()),
765 _ => PacketQueueItem::Batch(packets),
766 };
767 self.send_item(tx, item).map_err(|_| ())
768 }
769
770 fn send_bulk_packet_items(&self, mut packets: PacketBatch) -> Result<(), ()> {
771 let packet_count = packets.packets.len();
772 if packet_count == 0 {
773 return Ok(());
774 }
775
776 let granted = self.try_reserve_bulk_packet_prefix(packet_count);
777 if granted == 0 {
778 crate::perf_profile::record_event_count(
779 crate::perf_profile::Event::TransportBulkDropped,
780 packet_count as u64,
781 );
782 return Ok(());
783 }
784
785 if granted < packet_count {
786 let dropped = packet_count - granted;
787 let _dropped_tail = packets.packets.split_off(granted);
788 crate::perf_profile::record_event_count(
789 crate::perf_profile::Event::TransportBulkDropped,
790 dropped as u64,
791 );
792 }
793
794 let item = match packets.packets.len() {
795 0 => return Ok(()),
796 _ => PacketQueueItem::Batch(packets),
797 };
798 self.send_reserved_item(PacketQueueTx::Bulk, item, Some(granted))
799 .map_err(|_| ())
800 }
801
802 fn send_item(&self, tx: PacketQueueTx, item: PacketQueueItem) -> Result<(), PacketQueueItem> {
803 let packet_count = item.packet_count();
804 let bulk_reserved = if matches!(tx, PacketQueueTx::Bulk) && packet_count > 0 {
805 if !self.try_reserve_bulk_packets(packet_count) {
806 crate::perf_profile::record_event_count(
807 crate::perf_profile::Event::TransportBulkDropped,
808 packet_count as u64,
809 );
810 return Ok(());
811 }
812 Some(packet_count)
813 } else {
814 None
815 };
816 self.send_reserved_item(tx, item, bulk_reserved)
817 }
818
819 fn send_reserved_item(
820 &self,
821 tx: PacketQueueTx,
822 item: PacketQueueItem,
823 bulk_reserved: Option<usize>,
824 ) -> Result<(), PacketQueueItem> {
825 let packet_count = item.packet_count();
826 debug_assert_eq!(
827 bulk_reserved,
828 matches!(tx, PacketQueueTx::Bulk)
829 .then_some(packet_count)
830 .filter(|count| *count > 0)
831 );
832 let priority_reserved = matches!(tx, PacketQueueTx::Priority)
833 .then_some(packet_count)
834 .filter(|count| *count > 0);
835 if let Some(count) = priority_reserved {
836 self.priority_queued_packets.fetch_add(count, Relaxed);
837 }
838
839 let tracked_count = if self.track_backlog {
840 Some(packet_count)
841 } else {
842 None
843 };
844 let previous = tracked_count.map(|count| self.queued_packets.fetch_add(count, Relaxed));
845 match tx.try_send(self, item) {
846 Ok(()) => {
847 if let (Some(count), Some(previous)) = (tracked_count, previous) {
848 let queued = previous.saturating_add(count);
849 if previous < TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER
850 && queued >= TRANSPORT_CHANNEL_BACKLOG_HIGH_WATER
851 {
852 crate::perf_profile::record_event(
853 crate::perf_profile::Event::TransportChannelBacklogHigh,
854 );
855 }
856 }
857 Ok(())
858 }
859 Err(PacketSendFailure::Closed(item)) => {
860 if let Some(count) = tracked_count {
861 self.queued_packets.fetch_sub(count, Relaxed);
862 }
863 if let Some(count) = priority_reserved {
864 release_priority_packets(&self.priority_queued_packets, count);
865 }
866 if let Some(count) = bulk_reserved {
867 self.release_bulk_packets(count);
868 }
869 Err(item)
870 }
871 Err(PacketSendFailure::DroppedBulk(dropped_count)) => {
872 if let Some(count) = tracked_count {
873 self.queued_packets.fetch_sub(count, Relaxed);
874 }
875 if let Some(count) = priority_reserved {
876 release_priority_packets(&self.priority_queued_packets, count);
877 }
878 if let Some(count) = bulk_reserved {
879 self.release_bulk_packets(count);
880 }
881 crate::perf_profile::record_event_count(
882 crate::perf_profile::Event::TransportBulkDropped,
883 dropped_count as u64,
884 );
885 Ok(())
886 }
887 }
888 }
889
890 fn try_reserve_bulk_packets(&self, count: usize) -> bool {
891 self.bulk_queued_packets
892 .fetch_update(Relaxed, Relaxed, |current| {
893 current
894 .checked_add(count)
895 .filter(|next| *next <= self.bulk_packet_capacity)
896 })
897 .is_ok()
898 }
899
900 fn try_reserve_bulk_packet_prefix(&self, requested: usize) -> usize {
901 if requested == 0 {
902 return 0;
903 }
904
905 let mut current = self.bulk_queued_packets.load(Relaxed);
906 loop {
907 let available = self.bulk_packet_capacity.saturating_sub(current);
908 let granted = requested.min(available);
909 if granted == 0 {
910 return 0;
911 }
912 match self.bulk_queued_packets.compare_exchange_weak(
913 current,
914 current + granted,
915 Relaxed,
916 Relaxed,
917 ) {
918 Ok(_) => return granted,
919 Err(actual) => current = actual,
920 }
921 }
922 }
923
924 fn release_bulk_packets(&self, count: usize) {
925 release_reserved_bulk_packets(&self.bulk_queued_packets, count);
926 }
927}
928
929impl PacketRx {
930 pub(crate) fn priority_queued_packets(&self) -> usize {
931 self.priority_queued_packets.load(Relaxed)
932 }
933
934 pub(crate) fn priority_ready_packets(&self) -> usize {
935 self.pending_priority
936 .as_ref()
937 .map_or(0, |packets| packets.batch.packets.len())
938 .saturating_add(self.priority_queued_packets())
939 }
940
941 pub async fn recv(&mut self) -> Option<ReceivedPacket> {
942 loop {
943 match self.try_recv() {
944 Ok(packet) => return Some(packet),
945 Err(TryRecvError::Disconnected) => return None,
946 Err(TryRecvError::Empty) => {}
947 }
948
949 tokio::select! {
950 biased;
951 item = self.priority.recv(), if !self.priority_closed => {
952 match item {
953 Some(item) => {
954 if let Some(packet) = self.packet_from_item(item, PacketLane::Priority) {
955 return Some(packet);
956 }
957 }
958 None => self.priority_closed = true,
959 }
960 }
961 item = self.bulk.recv(), if !self.bulk_closed => {
962 match item {
963 Some(item) => {
964 if let Some(packet) = self.packet_from_item(item, PacketLane::Bulk) {
965 return Some(packet);
966 }
967 }
968 None => self.bulk_closed = true,
969 }
970 }
971 }
972 }
973 }
974
975 pub fn try_recv(&mut self) -> Result<ReceivedPacket, TryRecvError> {
976 if let Some(packet) = Self::take_pending(&mut self.pending_priority) {
977 return Ok(packet);
978 }
979
980 if self.should_probe_priority() {
981 match self.priority.try_recv() {
982 Ok(item) => {
983 if let Some(packet) = self.packet_from_item(item, PacketLane::Priority) {
984 return Ok(packet);
985 }
986 }
987 Err(TryRecvError::Empty) => {}
988 Err(TryRecvError::Disconnected) => {
989 self.priority_closed = true;
990 }
991 }
992 }
993
994 if let Some(packet) = Self::take_pending(&mut self.pending_bulk) {
995 return Ok(packet);
996 }
997
998 match self.bulk.try_recv() {
999 Ok(item) => self
1000 .packet_from_item(item, PacketLane::Bulk)
1001 .ok_or(TryRecvError::Empty),
1002 Err(TryRecvError::Empty) => {
1003 if self.priority_closed && self.bulk_closed {
1004 Err(TryRecvError::Disconnected)
1005 } else {
1006 Err(TryRecvError::Empty)
1007 }
1008 }
1009 Err(TryRecvError::Disconnected) => {
1010 self.bulk_closed = true;
1011 if self.priority_closed {
1012 Err(TryRecvError::Disconnected)
1013 } else {
1014 Err(TryRecvError::Empty)
1015 }
1016 }
1017 }
1018 }
1019
1020 pub(crate) fn drain_ready<F>(&mut self, limit: usize, mut consume: F) -> usize
1021 where
1022 F: FnMut(ReceivedPacket) -> bool,
1023 {
1024 let mut drained = 0usize;
1025 while drained < limit {
1026 if !self.drain_pending_priority(limit, &mut drained, &mut consume) {
1027 break;
1028 }
1029 if drained >= limit {
1030 break;
1031 }
1032
1033 if self.should_probe_priority() {
1034 match self.priority.try_recv() {
1035 Ok(item) => {
1036 if !self.drain_item(
1037 item,
1038 PacketLane::Priority,
1039 limit,
1040 &mut drained,
1041 &mut consume,
1042 ) {
1043 break;
1044 }
1045 continue;
1046 }
1047 Err(TryRecvError::Empty) => {}
1048 Err(TryRecvError::Disconnected) => {
1049 self.priority_closed = true;
1050 }
1051 }
1052 }
1053 if drained >= limit {
1054 break;
1055 }
1056
1057 if !self.drain_pending_bulk(limit, &mut drained, &mut consume) {
1058 break;
1059 }
1060 if drained >= limit {
1061 break;
1062 }
1063
1064 match self.bulk.try_recv() {
1065 Ok(item) => {
1066 if !self.drain_item(item, PacketLane::Bulk, limit, &mut drained, &mut consume) {
1067 break;
1068 }
1069 }
1070 Err(TryRecvError::Empty) => break,
1071 Err(TryRecvError::Disconnected) => {
1072 self.bulk_closed = true;
1073 break;
1074 }
1075 }
1076 }
1077 drained
1078 }
1079
1080 fn packet_from_item(
1081 &mut self,
1082 item: PacketQueueItem,
1083 lane: PacketLane,
1084 ) -> Option<ReceivedPacket> {
1085 item.record_dequeue_wait(lane);
1086 let packet_count = item.packet_count();
1087 if self.track_backlog {
1088 self.queued_packets.fetch_sub(packet_count, Relaxed);
1089 }
1090 if matches!(lane, PacketLane::Priority) {
1091 release_priority_packets(&self.priority_queued_packets, packet_count);
1092 }
1093 if matches!(lane, PacketLane::Bulk) {
1094 release_reserved_bulk_packets(&self.bulk_queued_packets, packet_count);
1095 }
1096 let rx_loop_owned_at = crate::perf_profile::stamp();
1097 match item {
1098 PacketQueueItem::One(mut packet) => {
1099 packet.trace_rx_loop_owned_at = rx_loop_owned_at;
1100 Some(packet)
1101 }
1102 PacketQueueItem::Batch(packets) => {
1103 let mut pending = PendingPackets::new(packets, rx_loop_owned_at);
1104 let packet = pending.next()?;
1105 if !pending.batch.packets.is_empty() {
1106 match lane {
1107 PacketLane::Priority => self.pending_priority = Some(pending),
1108 PacketLane::Bulk => self.pending_bulk = Some(pending),
1109 }
1110 }
1111 Some(packet)
1112 }
1113 }
1114 }
1115
1116 fn drain_item<F>(
1117 &mut self,
1118 item: PacketQueueItem,
1119 lane: PacketLane,
1120 limit: usize,
1121 drained: &mut usize,
1122 consume: &mut F,
1123 ) -> bool
1124 where
1125 F: FnMut(ReceivedPacket) -> bool,
1126 {
1127 if let Some(packet) = self.packet_from_item(item, lane) {
1128 *drained += 1;
1129 if !consume(packet) {
1130 return false;
1131 }
1132 }
1133
1134 match lane {
1135 PacketLane::Priority => self.drain_pending_priority(limit, drained, consume),
1136 PacketLane::Bulk => self.drain_pending_bulk(limit, drained, consume),
1137 }
1138 }
1139
1140 fn drain_pending_priority<F>(
1141 &mut self,
1142 limit: usize,
1143 drained: &mut usize,
1144 consume: &mut F,
1145 ) -> bool
1146 where
1147 F: FnMut(ReceivedPacket) -> bool,
1148 {
1149 while *drained < limit {
1150 let Some(packet) = Self::take_pending(&mut self.pending_priority) else {
1151 return true;
1152 };
1153 *drained += 1;
1154 if !consume(packet) {
1155 return false;
1156 }
1157 }
1158 true
1159 }
1160
1161 fn drain_pending_bulk<F>(&mut self, limit: usize, drained: &mut usize, consume: &mut F) -> bool
1162 where
1163 F: FnMut(ReceivedPacket) -> bool,
1164 {
1165 while *drained < limit {
1166 if self.should_probe_priority() {
1167 return true;
1168 }
1169 let Some(packet) = Self::take_pending(&mut self.pending_bulk) else {
1170 return true;
1171 };
1172 *drained += 1;
1173 if !consume(packet) {
1174 return false;
1175 }
1176 }
1177 true
1178 }
1179
1180 fn should_probe_priority(&self) -> bool {
1181 !self.priority_closed
1182 && (self.priority_queued_packets.load(Relaxed) > 0 || self.bulk_closed)
1183 }
1184
1185 fn take_pending(pending: &mut Option<PendingPackets>) -> Option<ReceivedPacket> {
1186 let packets = pending.as_mut()?;
1187 let packet = packets.next();
1188 if packets.batch.packets.is_empty() {
1189 *pending = None;
1190 }
1191 packet
1192 }
1193}
1194
1195#[inline]
1196fn packet_channel_tracks_backlog() -> bool {
1197 cfg!(test) || crate::perf_profile::enabled()
1198}
1199
1200fn release_reserved_bulk_packets(counter: &AtomicUsize, count: usize) {
1201 if count == 0 {
1202 return;
1203 }
1204
1205 let previous = counter.fetch_sub(count, Relaxed);
1206 debug_assert!(
1207 previous >= count,
1208 "transport bulk queued packet accounting underflow"
1209 );
1210}
1211
1212fn release_priority_packets(counter: &AtomicUsize, count: usize) {
1213 if count == 0 {
1214 return;
1215 }
1216
1217 let previous = counter.fetch_sub(count, Relaxed);
1218 debug_assert!(
1219 previous >= count,
1220 "transport priority queued packet accounting underflow"
1221 );
1222}
1223
1224pub fn packet_channel(buffer: usize) -> (PacketTx, PacketRx) {
1230 let (priority_tx, priority_rx) = tokio::sync::mpsc::unbounded_channel();
1231 let (bulk_tx, bulk_rx) = tokio::sync::mpsc::channel(buffer.max(1));
1232 let priority_queued_packets = Arc::new(AtomicUsize::new(0));
1233 let queued_packets = Arc::new(AtomicUsize::new(0));
1234 let bulk_queued_packets = Arc::new(AtomicUsize::new(0));
1235 let track_backlog = packet_channel_tracks_backlog();
1236 (
1237 PacketTx {
1238 priority: priority_tx,
1239 bulk: bulk_tx,
1240 fast_ingress: None,
1241 batch_pool: PacketBatchPool::new(),
1242 #[cfg(any(test, target_os = "linux", target_os = "macos"))]
1243 buffer_pool: PacketBufferPool::new(),
1244 priority_queued_packets: Arc::clone(&priority_queued_packets),
1245 queued_packets: Arc::clone(&queued_packets),
1246 bulk_queued_packets: Arc::clone(&bulk_queued_packets),
1247 bulk_packet_capacity: buffer.max(1),
1248 track_backlog,
1249 },
1250 PacketRx {
1251 priority: priority_rx,
1252 bulk: bulk_rx,
1253 priority_queued_packets,
1254 queued_packets,
1255 bulk_queued_packets,
1256 track_backlog,
1257 pending_priority: None,
1258 pending_bulk: None,
1259 priority_closed: false,
1260 bulk_closed: false,
1261 },
1262 )
1263}
1264
1265#[cfg(test)]
1266mod tests;