1use crate::control::queries;
4use crate::control::{ControlMessage, ControlSenders, ControlSocket, commands};
5use crate::dataplane::DataplaneFastIngressRx;
6use crate::node::{
7 EndpointDataBatchRx, EndpointEventSender, Node, NodeError, endpoint_data_batch_channel,
8};
9use crate::transport::PacketRx;
10use crate::upper::tun::TunOutboundRx;
11use std::time::{Duration, Instant};
12use tokio::sync::mpsc::Receiver;
13use tracing::{debug, info, warn};
14
15mod budget;
16mod dataplane;
17mod drain;
18
19#[cfg(test)]
20mod tests;
21
22use budget::*;
23use drain::*;
24
25pub(in crate::node) struct RxLoopDataplaneIo<'a> {
26 packet_rx: &'a mut PacketRx,
27 dataplane_fast_ingress_rx: &'a mut DataplaneFastIngressRx,
28 endpoint_data_rx: &'a mut EndpointDataBatchRx,
29 tun_outbound_rx: &'a mut TunOutboundRx,
30 endpoint_tx: &'a EndpointEventSender,
31}
32
33struct RxLoopDataplaneRuntime {
34 packet_rx: PacketRx,
35 dataplane_fast_ingress_rx: DataplaneFastIngressRx,
36 endpoint_data_rx: EndpointDataBatchRx,
37 tun_outbound_rx: TunOutboundRx,
38 endpoint_tx: EndpointEventSender,
39}
40
41impl RxLoopDataplaneRuntime {
42 fn io(&mut self) -> RxLoopDataplaneIo<'_> {
43 RxLoopDataplaneIo {
44 packet_rx: &mut self.packet_rx,
45 dataplane_fast_ingress_rx: &mut self.dataplane_fast_ingress_rx,
46 endpoint_data_rx: &mut self.endpoint_data_rx,
47 tun_outbound_rx: &mut self.tun_outbound_rx,
48 endpoint_tx: &self.endpoint_tx,
49 }
50 }
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub(in crate::node) struct RxLoopDataplaneTurnLimits {
55 packet: usize,
56 endpoint: usize,
57 tun: usize,
58 crypto: usize,
59}
60
61impl RxLoopDataplaneTurnLimits {
62 pub(in crate::node) fn new(packet: usize, endpoint: usize, tun: usize, crypto: usize) -> Self {
63 Self {
64 packet,
65 endpoint,
66 tun,
67 crypto,
68 }
69 }
70}
71
72#[cfg(test)]
73pub(in crate::node) fn rx_loop_dataplane_io<'a>(
74 packet_rx: &'a mut PacketRx,
75 dataplane_fast_ingress_rx: &'a mut DataplaneFastIngressRx,
76 endpoint_data_rx: &'a mut EndpointDataBatchRx,
77 tun_outbound_rx: &'a mut TunOutboundRx,
78 endpoint_tx: &'a EndpointEventSender,
79) -> RxLoopDataplaneIo<'a> {
80 RxLoopDataplaneIo {
81 packet_rx,
82 dataplane_fast_ingress_rx,
83 endpoint_data_rx,
84 tun_outbound_rx,
85 endpoint_tx,
86 }
87}
88
89impl Node {
90 pub async fn run_rx_loop(&mut self) -> Result<(), NodeError> {
110 let packet_rx = self.packet_rx.take().ok_or(NodeError::NotStarted)?;
111
112 let (tun_outbound_rx, _tun_guard) = match self.tun_outbound_rx.take() {
116 Some(rx) => (rx, None),
117 None => {
118 let (tx, rx) = crate::upper::tun::tun_outbound_channel(1);
119 (rx, Some(tx))
120 }
121 };
122
123 let (mut dns_identity_rx, _dns_guard) = match self.dns_identity_rx.take() {
126 Some(rx) => (rx, None),
127 None => {
128 let (tx, rx) = tokio::sync::mpsc::channel(1);
129 (rx, Some(tx))
130 }
131 };
132
133 let (mut endpoint_control_rx, _endpoint_control_guard) =
136 match self.endpoint_control_rx.take() {
137 Some(rx) => (rx, None),
138 None => {
139 let (tx, rx) = tokio::sync::mpsc::channel(1);
140 (rx, Some(tx))
141 }
142 };
143 let (endpoint_data_rx, _endpoint_data_guard) = match self.endpoint_data_rx.take() {
144 Some(rx) => (rx, None),
145 None => {
146 let (tx, rx) = endpoint_data_batch_channel(1);
147 (rx, Some(tx))
148 }
149 };
150 let (dataplane_fast_ingress_rx, _dataplane_fast_ingress_guard) =
151 match self.dataplane_fast_ingress_rx.take() {
152 Some(rx) => (rx, None),
153 None => {
154 let (tx, rx) = tokio::sync::mpsc::channel(1);
155 (rx, Some(tx))
156 }
157 };
158
159 let mut tick =
160 tokio::time::interval(Duration::from_secs(self.config.node.tick_interval_secs));
161 tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
162 let mut maintenance_state = RxLoopMaintenanceState::default();
163
164 let (control_query_tx, mut control_query_rx) =
168 tokio::sync::mpsc::channel::<ControlMessage>(32);
169 let (control_command_tx, mut control_command_rx) =
170 tokio::sync::mpsc::channel::<ControlMessage>(32);
171
172 if self.config.node.control.enabled {
173 let config = self.config.node.control.clone();
174 let senders = ControlSenders::new(control_query_tx.clone(), control_command_tx.clone());
175 tokio::spawn(async move {
176 match ControlSocket::bind(&config) {
177 Ok(socket) => {
178 socket.accept_loop_split(senders).await;
179 }
180 Err(e) => {
181 warn!(error = %e, "Failed to bind control socket");
182 }
183 }
184 });
185 }
186 drop(control_query_tx);
188 drop(control_command_tx);
189
190 let dataplane_endpoint_tx = self.endpoint_events.sender().unwrap_or_else(|| {
191 let (tx, rx) = EndpointEventSender::channel(1);
192 drop(rx);
193 tx
194 });
195 let dataplane_readiness_notify = self.dataplane.readiness_notify();
196 let mut dataplane_runtime = RxLoopDataplaneRuntime {
197 packet_rx,
198 dataplane_fast_ingress_rx,
199 endpoint_data_rx,
200 tun_outbound_rx,
201 endpoint_tx: dataplane_endpoint_tx,
202 };
203
204 info!("RX event loop started");
205 crate::perf_profile::maybe_spawn_reporter();
207 tick.tick().await;
211
212 loop {
213 tokio::select! {
214 biased;
215 _ = tick.tick() => {
221 let drained = {
222 let mut dataplane_io = dataplane_runtime.io();
223 self.drain_rx_loop_data_queues(
224 &mut dataplane_io,
225 ENDPOINT_DRAIN_BUDGET,
226 ).await
227 };
228 if drained.has_drained() {
229 maintenance_state.record_data_activity(Instant::now());
230 debug!(
231 drained = drained.total(),
232 drained_packets = drained.packets,
233 drained_tun = drained.tun,
234 drained_endpoint = drained.endpoint,
235 "Drained queued packets before rx-loop maintenance"
236 );
237 }
238 let maintenance_plan = maintenance_state.plan_maintenance(
239 drained,
240 Instant::now(),
241 RX_LOOP_RECENT_DATA_ACTIVITY_WINDOW,
242 RX_LOOP_SLOW_MAINTENANCE_IDLE_TIMEOUT,
243 RX_LOOP_SLOW_MAINTENANCE_BUSY_TIMEOUT,
244 );
245
246 let slow_timed_out = self.run_rx_loop_maintenance_tick(
247 maintenance_plan,
248 ).await;
249 maintenance_state.record_maintenance_result(
250 maintenance_plan.data_pressure(),
251 slow_timed_out,
252 );
253
254 let post_drained = {
255 let mut dataplane_io = dataplane_runtime.io();
256 self.drain_rx_loop_data_queues(
257 &mut dataplane_io,
258 PACKET_DRAIN_BUDGET,
259 ).await
260 };
261 if post_drained.has_drained() {
262 maintenance_state.record_data_activity(Instant::now());
263 debug!(
264 drained = post_drained.total(),
265 drained_packets = post_drained.packets,
266 drained_tun = post_drained.tun,
267 drained_endpoint = post_drained.endpoint,
268 "Drained queued packets after rx-loop maintenance"
269 );
270 }
271 }
272 Some(message) = control_query_rx.recv() => {
273 self.drain_control_queries(
274 &mut control_query_rx,
275 Some(message),
276 ENDPOINT_DRAIN_BUDGET,
277 ).await;
278 }
279 Some(command) = endpoint_control_rx.recv() => {
284 self.handle_endpoint_control(command).await;
285 }
286 packet = dataplane_runtime.packet_rx.recv() => {
287 match packet {
288 Some(p) => {
289 let latency_packet = p.is_transport_priority();
290 let mut firsts = crate::dataplane::DataplaneLiveTurnFirsts {
291 raw_packet: Some(p),
292 ..Default::default()
293 };
294 if let Ok(packet) = dataplane_runtime.tun_outbound_rx.try_recv() {
295 firsts.tun_packet = Some(packet);
296 }
297 let latency_work_ready = latency_packet
298 || dataplane_runtime.packet_rx.priority_ready_packets() > 0;
299 if latency_work_ready {
300 let packet_budget = packet_drain_budget(true);
301 let endpoint_budget = endpoint_drain_budget(packet_budget);
302 let tun_budget = tun_drain_budget(packet_budget);
303 let crypto_budget = mixed_dataplane_crypto_budget(
304 packet_budget,
305 endpoint_budget,
306 tun_budget,
307 );
308 let mut turn = {
309 let mut dataplane_io = dataplane_runtime.io();
310 self.drain_dataplane_turn_with_firsts(
311 &mut dataplane_io,
312 firsts,
313 RxLoopDataplaneTurnLimits::new(
314 packet_budget,
315 endpoint_budget,
316 tun_budget,
317 crypto_budget,
318 ),
319 ).await
320 };
321 self.finish_dataplane_turn(
322 &mut turn,
323 &mut maintenance_state,
324 &mut control_query_rx,
325 CONTROL_QUERY_INTERLEAVE_BUDGET,
326 ).await;
327 } else {
328 firsts.raw_ingress_prefetch = true;
329 let mut dataplane_io = dataplane_runtime.io();
330 self.service_dataplane_bulk_turns(
331 &mut dataplane_io,
332 firsts,
333 &mut maintenance_state,
334 &mut control_query_rx,
335 ).await;
336 }
337 }
338 None => break, }
340 }
341 Some(fast_ingress) = dataplane_runtime.dataplane_fast_ingress_rx.recv() => {
342 let mut dataplane_io = dataplane_runtime.io();
343 self.service_dataplane_bulk_turns(
344 &mut dataplane_io,
345 crate::dataplane::DataplaneLiveTurnFirsts {
346 fast_ingress: Some(fast_ingress),
347 ..Default::default()
348 },
349 &mut maintenance_state,
350 &mut control_query_rx,
351 ).await;
352 }
353 _ = dataplane_readiness_notify.notified() => {
354 Box::pin(self.flush_pending_local_rendezvous_sync()).await;
355 let mut dataplane_io = dataplane_runtime.io();
356 self.service_dataplane_completion_turns(
357 &mut dataplane_io,
358 &mut maintenance_state,
359 &mut control_query_rx,
360 ).await;
361 }
362 Some(ipv6_packet) = dataplane_runtime.tun_outbound_rx.recv() => {
363 let tun_budget = tun_drain_budget(LATENCY_PACKET_DRAIN_BUDGET);
364 let mut turn = {
365 let mut dataplane_io = dataplane_runtime.io();
366 self.drain_dataplane_turn_with_firsts(
367 &mut dataplane_io,
368 crate::dataplane::DataplaneLiveTurnFirsts {
369 tun_packet: Some(ipv6_packet),
370 ..Default::default()
371 },
372 RxLoopDataplaneTurnLimits::new(0, 0, tun_budget, tun_budget),
373 ).await
374 };
375 self.finish_dataplane_turn(
376 &mut turn,
377 &mut maintenance_state,
378 &mut control_query_rx,
379 0,
380 ).await;
381 }
382 Some(identity) = dns_identity_rx.recv() => {
383 debug!(
384 node_addr = %identity.node_addr,
385 "Registering identity from DNS resolution"
386 );
387 self.register_identity(identity.node_addr, identity.pubkey);
388 }
389 Some(batch) = dataplane_runtime.endpoint_data_rx.recv() => {
390 let mut turn = {
391 let mut dataplane_io = dataplane_runtime.io();
392 self.drain_dataplane_turn_with_firsts(
393 &mut dataplane_io,
394 crate::dataplane::DataplaneLiveTurnFirsts {
395 endpoint_data_batch: Some(batch),
396 ..Default::default()
397 },
398 RxLoopDataplaneTurnLimits::new(
399 0,
400 ENDPOINT_DRAIN_BUDGET,
401 0,
402 PACKET_DRAIN_BUDGET,
403 ),
404 ).await
405 };
406 self.finish_dataplane_turn(
407 &mut turn,
408 &mut maintenance_state,
409 &mut control_query_rx,
410 0,
411 ).await;
412 }
413 Some((request, response_tx)) = control_command_rx.recv() => {
414 let response = commands::dispatch(
415 self,
416 &request.command,
417 request.params.as_ref(),
418 ).await;
419 let _ = response_tx.send(response);
420 }
421 }
422 }
423
424 info!("RX event loop stopped (channel closed)");
425 Ok(())
426 }
427
428 async fn drain_rx_loop_data_queues(
429 &mut self,
430 io: &mut RxLoopDataplaneIo<'_>,
431 budget: usize,
432 ) -> RxLoopDataDrainStats {
433 let fast_ingress =
434 Self::take_dataplane_fast_ingress_batch(io.dataplane_fast_ingress_rx, budget);
435 let packet_budget = budget.max(
436 fast_ingress
437 .as_ref()
438 .map_or(0, |fast_ingress| fast_ingress.len()),
439 );
440 let endpoint_budget = endpoint_drain_budget(packet_budget);
441 let tun_budget = tun_drain_budget(packet_budget);
442 let crypto_budget =
443 mixed_dataplane_crypto_budget(packet_budget, endpoint_budget, tun_budget);
444 let mut turn = self
445 .drain_dataplane_turn_with_firsts(
446 io,
447 crate::dataplane::DataplaneLiveTurnFirsts {
448 fast_ingress,
449 ..Default::default()
450 },
451 RxLoopDataplaneTurnLimits::new(
452 packet_budget,
453 endpoint_budget,
454 tun_budget,
455 crypto_budget,
456 ),
457 )
458 .await;
459 let drained_packets = Self::dataplane_packet_activity(&turn);
460 let control_drained = Box::pin(self.process_dataplane_control_ingress(&mut turn)).await;
461 RxLoopDataDrainStats::new(
462 drained_packets,
463 turn.tun_source_drained(),
464 turn.endpoint_source_drained(),
465 control_drained,
466 )
467 }
468
469 fn take_dataplane_fast_ingress_batch(
470 dataplane_fast_ingress_rx: &mut crate::dataplane::DataplaneFastIngressRx,
471 limit: usize,
472 ) -> Option<crate::dataplane::DataplaneFastIngressBatch> {
473 let fast_ingress = dataplane_fast_ingress_rx.try_recv().ok()?;
474 Some(Self::coalesce_dataplane_fast_ingress(
475 fast_ingress,
476 dataplane_fast_ingress_rx,
477 limit,
478 ))
479 }
480
481 fn coalesce_dataplane_fast_ingress(
482 mut fast_ingress: crate::dataplane::DataplaneFastIngressBatch,
483 dataplane_fast_ingress_rx: &mut crate::dataplane::DataplaneFastIngressRx,
484 limit: usize,
485 ) -> crate::dataplane::DataplaneFastIngressBatch {
486 while fast_ingress.len() < limit {
487 let Ok(next) = dataplane_fast_ingress_rx.try_recv() else {
488 break;
489 };
490 fast_ingress.absorb(next);
491 }
492 fast_ingress
493 }
494
495 async fn service_dataplane_bulk_turns(
496 &mut self,
497 io: &mut RxLoopDataplaneIo<'_>,
498 firsts: crate::dataplane::DataplaneLiveTurnFirsts,
499 maintenance_state: &mut RxLoopMaintenanceState,
500 control_query_rx: &mut Receiver<ControlMessage>,
501 ) {
502 let started = Instant::now();
503 let mut firsts = Some(firsts);
504 let mut turns = 0usize;
505
506 loop {
507 if turns > 0
508 && (turns >= RX_LOOP_BULK_SERVICE_MAX_TURNS
509 || started.elapsed() >= RX_LOOP_BULK_SERVICE_MAX_ELAPSED
510 || io.packet_rx.priority_ready_packets() > 0)
511 {
512 break;
513 }
514
515 let packet_budget = PACKET_DRAIN_BUDGET;
516 let mut turn_firsts = firsts.take().unwrap_or_default();
517 turn_firsts.raw_ingress_prefetch = true;
518 turn_firsts.fast_ingress = match turn_firsts.fast_ingress.take() {
519 Some(fast_ingress) => Some(Self::coalesce_dataplane_fast_ingress(
520 fast_ingress,
521 io.dataplane_fast_ingress_rx,
522 packet_budget,
523 )),
524 None => Self::take_dataplane_fast_ingress_batch(
525 io.dataplane_fast_ingress_rx,
526 packet_budget,
527 ),
528 };
529 let packet_budget = packet_budget.max(
530 turn_firsts
531 .fast_ingress
532 .as_ref()
533 .map_or(0, |fast_ingress| fast_ingress.len()),
534 );
535 let endpoint_budget = endpoint_drain_budget(packet_budget);
536 let tun_budget = tun_drain_budget(packet_budget);
537 let crypto_budget =
538 mixed_dataplane_crypto_budget(packet_budget, endpoint_budget, tun_budget);
539
540 let mut turn = self
541 .drain_dataplane_turn_with_firsts(
542 io,
543 turn_firsts,
544 RxLoopDataplaneTurnLimits::new(
545 packet_budget,
546 endpoint_budget,
547 tun_budget,
548 crypto_budget,
549 ),
550 )
551 .await;
552 let raw_drained = Self::dataplane_raw_ingress_activity(&turn);
553 let control_activity = Self::dataplane_control_activity(&turn);
554 let completions_drained = turn.summary().completions();
555 let admission_dropped =
556 turn.summary().inbound_dropped() > 0 || turn.summary().outbound_dropped() > 0;
557 let keep_servicing = !admission_dropped
558 && (raw_drained >= packet_budget
559 || completions_drained >= crypto_budget
560 || turn.tun_source_drained() >= tun_budget
561 || turn.endpoint_source_drained() >= endpoint_budget);
562 let control_drained = self
563 .finish_dataplane_turn(
564 &mut turn,
565 maintenance_state,
566 control_query_rx,
567 CONTROL_QUERY_INTERLEAVE_BUDGET,
568 )
569 .await;
570 turns += 1;
571 let mut runnable_work = self.dataplane.has_runnable_work();
572
573 if control_drained == 0
574 && bulk_admission_pressure_relief_due(
575 admission_dropped,
576 runnable_work,
577 turns,
578 started.elapsed(),
579 io.packet_rx.priority_ready_packets(),
580 )
581 {
582 let mut relief_turn = self
583 .drain_dataplane_completion_turn(io, LATENCY_PACKET_DRAIN_BUDGET)
584 .await;
585 let relief_control_drained = self
586 .finish_dataplane_turn(&mut relief_turn, maintenance_state, control_query_rx, 0)
587 .await;
588 turns += 1;
589 runnable_work = self.dataplane.has_runnable_work();
590 if relief_control_drained > 0 || !runnable_work {
591 break;
592 }
593 }
594
595 if control_drained > 0
596 || admission_dropped
597 || (!keep_servicing && !runnable_work)
598 || (control_activity > 0 && !runnable_work)
599 {
600 break;
601 }
602 }
603 }
604
605 async fn service_dataplane_completion_turns(
606 &mut self,
607 io: &mut RxLoopDataplaneIo<'_>,
608 maintenance_state: &mut RxLoopMaintenanceState,
609 control_query_rx: &mut Receiver<ControlMessage>,
610 ) {
611 let started = Instant::now();
612 let mut turns = 0usize;
613
614 loop {
615 if turns > 0
616 && (turns >= RX_LOOP_BULK_SERVICE_MAX_TURNS
617 || started.elapsed() >= RX_LOOP_BULK_SERVICE_MAX_ELAPSED
618 || io.packet_rx.priority_ready_packets() > 0)
619 {
620 break;
621 }
622
623 let mut turn = self
624 .drain_dataplane_completion_turn(io, LATENCY_PACKET_DRAIN_BUDGET)
625 .await;
626 let control_drained = self
627 .finish_dataplane_turn(&mut turn, maintenance_state, control_query_rx, 0)
628 .await;
629 turns += 1;
630
631 let runnable_work = self.dataplane.has_runnable_work();
632 if control_drained > 0 || !runnable_work {
633 break;
634 }
635 }
636 }
637
638 async fn finish_dataplane_turn(
639 &mut self,
640 turn: &mut crate::dataplane::DataplaneLiveNodeTurn,
641 maintenance_state: &mut RxLoopMaintenanceState,
642 control_query_rx: &mut Receiver<ControlMessage>,
643 control_query_budget: usize,
644 ) -> usize {
645 let had_activity = turn.has_activity();
646 let control_drained = Box::pin(self.process_dataplane_control_ingress(turn))
647 .await
648 .saturating_add(Box::pin(self.drain_deferred_dataplane_control_turns()).await);
649 if control_drained > 0 && self.dataplane.has_deferred_raw_ingress() {
650 self.dataplane.readiness_notify().notify_one();
651 }
652 let query_drained = if control_query_budget > 0 {
653 self.drain_control_queries(control_query_rx, None, control_query_budget)
654 .await
655 } else {
656 0
657 };
658 if had_activity || control_drained > 0 {
659 maintenance_state.record_data_activity(Instant::now());
660 }
661 control_drained.saturating_add(query_drained)
662 }
663
664 async fn drain_control_queries(
665 &mut self,
666 control_query_rx: &mut Receiver<ControlMessage>,
667 first_message: Option<ControlMessage>,
668 budget: usize,
669 ) -> usize {
670 let mut drain = SingleLaneDrainCursor::new(first_message, budget);
671 while let Some((request, response_tx)) = drain.next(control_query_rx) {
672 let response = queries::dispatch(self, &request.command, request.params.as_ref());
673 let _ = response_tx.send(response);
674 }
675
676 drain.drained()
677 }
678
679 async fn run_rx_loop_maintenance_tick(&mut self, plan: RxLoopMaintenancePlan) -> bool {
680 if !rx_loop_fast_maintenance_within_budget(self.run_rx_loop_fast_maintenance_tick()).await {
681 crate::perf_profile::record_event(
682 crate::perf_profile::Event::RxLoopSlowMaintenanceTimeout,
683 );
684 self.mark_rx_loop_maintenance_timeout();
685 warn!(
686 timeout_ms = RX_LOOP_FAST_MAINTENANCE_TIMEOUT.as_millis() as u64,
687 data_pressure = plan.data_pressure(),
688 "RX loop liveness maintenance timed out; continuing packet processing"
689 );
690 return true;
691 }
692
693 let Some(slow_timeout) = plan.slow_timeout() else {
694 crate::perf_profile::record_event(
695 crate::perf_profile::Event::RxLoopSlowMaintenanceSkipped,
696 );
697 return false;
698 };
699
700 if tokio::time::timeout(slow_timeout, self.run_rx_loop_slow_maintenance_tick())
701 .await
702 .is_err()
703 {
704 crate::perf_profile::record_event(
705 crate::perf_profile::Event::RxLoopSlowMaintenanceTimeout,
706 );
707 self.mark_rx_loop_maintenance_timeout();
708 warn!(
709 timeout_ms = slow_timeout.as_millis() as u64,
710 data_pressure = plan.data_pressure(),
711 "RX loop slow maintenance timed out; continuing packet processing"
712 );
713 return true;
714 }
715 false
716 }
717
718 async fn run_rx_loop_fast_maintenance_tick(&mut self) {
719 self.check_timeouts();
720 let now_ms = Self::now_ms();
721 self.check_link_heartbeats().await;
725 self.reload_peer_acl();
726 self.resend_pending_handshakes(now_ms).await;
727 self.resend_pending_rekeys(now_ms).await;
728 self.resend_pending_session_handshakes(now_ms).await;
729 self.resend_pending_session_msg3(now_ms).await;
730 self.retry_pending_session_traffic().await;
731 self.purge_idle_sessions(now_ms);
732 self.purge_learned_routes(now_ms);
733 self.check_mmp_reports().await;
734 self.check_session_mmp_reports().await;
735 self.check_rekey().await;
736 self.check_session_rekey().await;
737 self.check_pending_lookups(now_ms).await;
738 self.poll_pending_connects().await;
739 self.process_pending_retries(now_ms).await;
740 self.poll_transport_discovery().await;
741 self.sample_transport_congestion();
742 }
743
744 async fn run_rx_loop_slow_maintenance_tick(&mut self) {
745 if let Some(delay) = rx_loop_slow_maintenance_fault_delay() {
746 tokio::time::sleep(delay).await;
747 }
748
749 self.poll_nostr_discovery().await;
754 self.poll_lan_discovery().await;
755 self.poll_local_rendezvous().await;
756 self.check_tree_state().await;
757 self.check_bloom_state().await;
758 self.compute_mesh_size();
759 self.record_stats_history();
760 }
761}
762
763async fn rx_loop_fast_maintenance_within_budget<F>(maintenance: F) -> bool
764where
765 F: std::future::Future<Output = ()>,
766{
767 tokio::time::timeout(RX_LOOP_FAST_MAINTENANCE_TIMEOUT, maintenance)
768 .await
769 .is_ok()
770}
771
772fn bulk_admission_pressure_relief_due(
773 admission_dropped: bool,
774 runnable_work: bool,
775 turns: usize,
776 elapsed: Duration,
777 priority_ready_packets: usize,
778) -> bool {
779 admission_dropped
780 && runnable_work
781 && turns < RX_LOOP_BULK_SERVICE_MAX_TURNS
782 && elapsed < RX_LOOP_BULK_SERVICE_MAX_ELAPSED
783 && priority_ready_packets == 0
784}