1use std::future::Future;
3use std::pin::Pin;
4use std::sync::{Arc, Mutex, Weak};
5use std::task::{Context, Poll, Waker};
6
7use futures::channel::oneshot;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum LinkSendError {
12 QueueFull,
13 DriverStopped,
14 Interrupted,
15 Draining,
16 InvalidPacket(crate::event::LinkDatagramError),
17 PacketBuildFailed,
18 NoRoute,
19 Rejected,
20 InterfaceUnavailable,
21 WriteFailed(String),
22}
23
24impl std::fmt::Display for LinkSendError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::QueueFull => f.write_str("outbound queue is full"),
28 Self::DriverStopped => f.write_str("driver stopped before transmission completed"),
29 Self::Interrupted => f.write_str("transmission interrupted before completion"),
30 Self::Draining => f.write_str("node is draining"),
31 Self::InvalidPacket(error) => write!(f, "{error}"),
32 Self::PacketBuildFailed => f.write_str("failed to build or encrypt outbound packet"),
33 Self::NoRoute => f.write_str("no outgoing interface route"),
34 Self::Rejected => f.write_str("outbound packet rejected by routing or policy"),
35 Self::InterfaceUnavailable => f.write_str("outbound interface is unavailable"),
36 Self::WriteFailed(error) => write!(f, "interface write failed: {error}"),
37 }
38 }
39}
40impl std::error::Error for LinkSendError {}
41
42#[must_use = "await the receipt (or call wait) to observe transmission completion"]
46pub struct LinkSendReceipt(oneshot::Receiver<Result<(), LinkSendError>>);
47
48impl Future for LinkSendReceipt {
49 type Output = Result<(), LinkSendError>;
50 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
51 Pin::new(&mut self.0)
52 .poll(cx)
53 .map(|result| result.unwrap_or(Err(LinkSendError::Interrupted)))
54 }
55}
56
57impl LinkSendReceipt {
58 pub fn wait(self) -> Result<(), LinkSendError> {
60 futures::executor::block_on(self)
61 }
62}
63
64#[must_use = "await the receipt (or call wait) to observe transmission completion"]
68pub struct PacketSendReceipt {
69 pub(crate) completion: LinkSendReceipt,
70 pub(crate) hash: rns_core::types::PacketHash,
71}
72impl PacketSendReceipt {
73 pub fn packet_hash(&self) -> rns_core::types::PacketHash {
74 self.hash
75 }
76 pub fn wait(self) -> Result<rns_core::types::PacketHash, LinkSendError> {
78 futures::executor::block_on(self)
79 }
80}
81impl Future for PacketSendReceipt {
82 type Output = Result<rns_core::types::PacketHash, LinkSendError>;
83 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
84 let hash = self.hash;
85 Pin::new(&mut self.completion)
86 .poll(cx)
87 .map(|r| r.map(|()| hash))
88 }
89}
90
91#[derive(Default)]
92pub(crate) struct Waiter(Mutex<Option<Waker>>);
93
94impl Waiter {
95 pub(crate) fn register(&self, cx: &Context<'_>) {
96 *self.0.lock().unwrap() = Some(cx.waker().clone());
97 }
98 pub(crate) fn wake(&self) {
99 let waker = self.0.lock().unwrap().take();
100 if let Some(waker) = waker {
101 waker.wake();
102 }
103 }
104}
105
106struct PoolState {
107 used: usize,
108 closed: bool,
109 waiters: Vec<Weak<Waiter>>,
110 completions: Vec<Weak<CompletionState>>,
111 links: std::collections::HashMap<[u8; 16], SendCounts>,
112}
113
114#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
115pub(crate) struct SendCounts {
116 pub pending: usize,
117 pub waiting: usize,
118}
119
120enum SendPhase {
121 Unadmitted,
122 Waiting,
123 Pending,
124}
125
126pub(crate) struct SendTracking {
127 pool: Arc<SendPool>,
128 link_id: [u8; 16],
129 phase: SendPhase,
130}
131
132impl SendTracking {
133 fn admit(&mut self) {
134 let mut state = self.pool.state.lock().unwrap();
135 if state.closed {
136 return;
137 }
138 let counts = state.links.entry(self.link_id).or_default();
139 match self.phase {
140 SendPhase::Waiting => counts.waiting -= 1,
141 SendPhase::Pending => return,
142 SendPhase::Unadmitted => {}
143 }
144 counts.pending += 1;
145 self.phase = SendPhase::Pending;
146 }
147}
148
149impl Drop for SendTracking {
150 fn drop(&mut self) {
151 let mut state = self.pool.state.lock().unwrap();
152 if let Some(counts) = state.links.get_mut(&self.link_id) {
153 match self.phase {
154 SendPhase::Waiting => counts.waiting -= 1,
155 SendPhase::Pending => counts.pending -= 1,
156 SendPhase::Unadmitted => {}
157 }
158 if counts.pending == 0 && counts.waiting == 0 {
159 state.links.remove(&self.link_id);
160 }
161 }
162 }
163}
164
165pub(crate) struct SendPool {
166 capacity: usize,
167 state: Mutex<PoolState>,
168}
169
170impl SendPool {
171 pub(crate) fn new(capacity: usize) -> Arc<Self> {
172 Arc::new(Self {
173 capacity: capacity.max(1),
174 state: Mutex::new(PoolState {
175 used: 0,
176 closed: false,
177 waiters: Vec::new(),
178 completions: Vec::new(),
179 links: std::collections::HashMap::new(),
180 }),
181 })
182 }
183
184 pub(crate) fn try_acquire(self: &Arc<Self>) -> Result<Permit, LinkSendError> {
185 let mut state = self.state.lock().unwrap();
186 if state.closed {
187 return Err(LinkSendError::DriverStopped);
188 }
189 if state.used == self.capacity {
190 return Err(LinkSendError::QueueFull);
191 }
192 state.used += 1;
193 Ok(Permit(self.clone()))
194 }
195
196 pub(crate) fn track(self: &Arc<Self>, link_id: [u8; 16], waiting: bool) -> SendTracking {
197 let mut state = self.state.lock().unwrap();
198 let phase = if waiting && !state.closed {
199 state.links.entry(link_id).or_default().waiting += 1;
200 SendPhase::Waiting
201 } else {
202 SendPhase::Unadmitted
203 };
204 SendTracking {
205 pool: self.clone(),
206 link_id,
207 phase,
208 }
209 }
210
211 pub(crate) fn snapshot(&self) -> std::collections::HashMap<[u8; 16], SendCounts> {
212 self.state.lock().unwrap().links.clone()
213 }
214
215 pub(crate) async fn acquire(self: &Arc<Self>) -> Result<Permit, LinkSendError> {
216 let waiter = Arc::new(Waiter::default());
217 {
218 let mut state = self.state.lock().unwrap();
219 state.waiters.retain(|waiter| waiter.strong_count() > 0);
220 state.waiters.push(Arc::downgrade(&waiter));
221 }
222 futures::future::poll_fn(|cx| {
223 waiter.register(cx);
224 match self.try_acquire() {
225 Err(LinkSendError::QueueFull) => Poll::Pending,
226 result => Poll::Ready(result),
227 }
228 })
229 .await
230 }
231
232 pub(crate) fn close(&self) {
233 let (waiters, completions) = {
234 let mut state = self.state.lock().unwrap();
235 state.closed = true;
236 state.links.clear();
237 (
238 std::mem::take(&mut state.waiters),
239 std::mem::take(&mut state.completions),
240 )
241 };
242 for waiter in waiters.into_iter().filter_map(|w| w.upgrade()) {
243 waiter.wake();
244 }
245 for completion in completions.into_iter().filter_map(|w| w.upgrade()) {
246 completion.finish(Err(LinkSendError::DriverStopped));
247 }
248 }
249
250 pub(crate) fn in_flight(&self) -> usize {
251 self.state.lock().unwrap().used
252 }
253}
254
255pub(crate) struct Permit(Arc<SendPool>);
256impl Drop for Permit {
257 fn drop(&mut self) {
258 let waiters = {
259 let mut state = self.0.state.lock().unwrap();
260 state.used -= 1;
261 state.waiters.retain(|waiter| waiter.strong_count() > 0);
262 state.waiters.clone()
263 };
264 for waiter in waiters.into_iter().filter_map(|w| w.upgrade()) {
265 waiter.wake();
266 }
267 }
268}
269
270#[doc(hidden)]
272pub struct Completion {
273 ratchet_lease: Option<crate::local_ratchet::AnnouncementLease>,
274 state: Arc<CompletionState>,
275 settled: bool,
276}
277
278struct CompletionData {
279 tx: oneshot::Sender<Result<(), LinkSendError>>,
280 tracking: Option<SendTracking>,
281 permit: Permit,
282 remaining: usize,
283 error: Option<LinkSendError>,
284}
285struct CompletionState(Mutex<Option<CompletionData>>);
286impl Drop for CompletionState {
287 fn drop(&mut self) {
288 self.finish(Err(LinkSendError::Interrupted));
290 }
291}
292impl CompletionState {
293 fn finish(&self, result: Result<(), LinkSendError>) {
294 let data = self.0.lock().unwrap().take();
295 if let Some(data) = data {
296 drop(data.tracking);
297 drop(data.permit);
298 let _ = data.tx.send(result);
299 }
300 }
301}
302
303impl Completion {
304 pub(crate) fn retain_ratchet(
305 &mut self,
306 lease: Option<crate::local_ratchet::AnnouncementLease>,
307 ) {
308 self.ratchet_lease = lease;
309 }
310
311 pub(crate) fn new(permit: Permit) -> (Self, LinkSendReceipt) {
312 Self::new_inner(permit, None)
313 }
314
315 pub(crate) fn new_tracked(permit: Permit, tracking: SendTracking) -> (Self, LinkSendReceipt) {
316 Self::new_inner(permit, Some(tracking))
317 }
318
319 fn new_inner(permit: Permit, tracking: Option<SendTracking>) -> (Self, LinkSendReceipt) {
320 let (tx, rx) = oneshot::channel();
321 let pool = permit.0.clone();
322 let completion = Arc::new(CompletionState(Mutex::new(Some(CompletionData {
323 tx,
324 tracking,
325 permit,
326 remaining: 1,
327 error: None,
328 }))));
329 {
330 let mut pool = pool.state.lock().unwrap();
331 pool.completions.retain(|w| w.strong_count() > 0);
332 if pool.closed {
333 drop(pool);
334 completion.finish(Err(LinkSendError::DriverStopped));
335 } else {
336 pool.completions.push(Arc::downgrade(&completion));
337 }
338 }
339 (
340 Self {
341 ratchet_lease: None,
342 state: completion,
343 settled: false,
344 },
345 LinkSendReceipt(rx),
346 )
347 }
348 pub(crate) fn finish(mut self, result: Result<(), LinkSendError>) {
349 self.settle(result);
350 }
351
352 pub(crate) fn branch(&self) -> Self {
355 if let Some(data) = self.state.0.lock().unwrap().as_mut() {
356 data.remaining += 1;
357 }
358 Self {
359 ratchet_lease: self.ratchet_lease.clone(),
360 state: self.state.clone(),
361 settled: false,
362 }
363 }
364
365 fn settle(&mut self, result: Result<(), LinkSendError>) {
366 self.ratchet_lease = None;
369 self.settled = true;
370 let final_data = {
371 let mut guard = self.state.0.lock().unwrap();
372 let Some(data) = guard.as_mut() else { return };
373 if data.error.is_none() {
374 data.error = result.err();
375 }
376 data.remaining -= 1;
377 if data.remaining == 0 {
378 guard.take()
379 } else {
380 None
381 }
382 };
383 if let Some(data) = final_data {
384 drop(data.tracking);
385 drop(data.permit);
386 let _ = data.tx.send(data.error.map_or(Ok(()), Err));
387 }
388 }
389 pub(crate) fn is_finished(&self) -> bool {
390 self.state.0.lock().unwrap().is_none()
391 }
392
393 pub(crate) fn admit(&self) {
394 if let Some(data) = self.state.0.lock().unwrap().as_mut() {
395 if let Some(tracking) = data.tracking.as_mut() {
396 tracking.admit();
397 }
398 }
399 }
400}
401
402impl Drop for Completion {
403 fn drop(&mut self) {
404 if !self.settled {
405 self.settle(Err(LinkSendError::Interrupted));
406 }
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413 use futures::{executor::block_on, FutureExt};
414
415 #[test]
416 fn fanout_keeps_capacity_until_all_writes_settle() {
417 let pool = SendPool::new(1);
418 let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
419 let first = completion.branch();
420 let last = completion.branch();
421 completion.finish(Ok(()));
422 first.finish(Err(LinkSendError::WriteFailed("first failed".into())));
423 assert!((&mut receipt).now_or_never().is_none());
424 assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
425 last.finish(Ok(()));
426 assert_eq!(
427 receipt.wait(),
428 Err(LinkSendError::WriteFailed("first failed".into()))
429 );
430 assert_eq!(pool.in_flight(), 0);
431
432 let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
433 let child = completion.branch();
434 completion.finish(Ok(()));
435 drop(child);
436 assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
437 assert_eq!(pool.in_flight(), 0);
438
439 let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
440 let child = completion.branch();
441 pool.close();
442 assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
443 assert_eq!(pool.in_flight(), 0);
444 drop((completion, child));
445 }
446
447 #[test]
448 fn packet_and_announce_admission_is_bounded_and_runtime_independent() {
449 use crate::event::Event;
450 let (tx, rx) = crate::event::channel_with_capacity(1);
451 let node = crate::RnsNode::from_parts(
452 tx.clone(),
453 std::thread::spawn(|| {}),
454 None,
455 Arc::new(std::sync::atomic::AtomicU64::new(1000)),
456 Arc::new(std::sync::atomic::AtomicBool::new(true)),
457 );
458 let dest = crate::Destination::plain("confirmed", &["packet"]);
459 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
460 let announce_dest = crate::Destination::single_in(
461 "confirmed",
462 &["announce"],
463 crate::IdentityHash(*identity.hash()),
464 );
465 drop(node.announce(&announce_dest, &identity, None));
466 assert!(
467 rx.try_recv().is_err(),
468 "unpolled sends must have no side effects"
469 );
470 tx.send(Event::Tick).unwrap();
471 assert!(matches!(
472 node.try_announce(&announce_dest, &identity, None),
473 Err(LinkSendError::QueueFull)
474 ));
475 let mut cancelled = Box::pin(node.announce(&announce_dest, &identity, None));
476 assert!(cancelled.as_mut().now_or_never().is_none());
477 drop(cancelled);
478 assert_eq!(tx.link_send_pool().in_flight(), 0);
479 assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
480 assert!(
481 rx.try_recv().is_err(),
482 "cancelled admission must not update replay state"
483 );
484 let receipt = node.try_send_packet(&dest, b"first").unwrap();
485 let hash = receipt.packet_hash();
486 let Event::SendOutboundTracked {
487 completion: first, ..
488 } = rx.try_recv().unwrap()
489 else {
490 panic!("expected tracked packet")
491 };
492 assert!(matches!(
493 node.try_announce(&announce_dest, &identity, None),
494 Err(LinkSendError::QueueFull)
495 ));
496 let mut announce = Box::pin(node.announce(&announce_dest, &identity, None));
497 assert!(announce.as_mut().now_or_never().is_none());
498 assert!(rx.try_recv().is_err());
499 first.finish(Ok(()));
500 assert_eq!(receipt.wait().unwrap(), hash);
501 assert!(announce.as_mut().now_or_never().is_none());
502 let Event::SendOutboundTracked {
503 completion, replay, ..
504 } = rx.try_recv().unwrap()
505 else {
506 panic!("expected tracked announce")
507 };
508 assert_eq!(replay.unwrap().dest_hash, announce_dest.hash.0);
509 assert!(matches!(
510 node.try_send_packet(&dest, b"full"),
511 Err(LinkSendError::QueueFull)
512 ));
513 completion.finish(Ok(()));
514 block_on(announce).unwrap();
515 let receipt = node.try_announce(&announce_dest, &identity, None).unwrap();
516 let Event::SendOutboundTracked { completion, .. } = rx.try_recv().unwrap() else {
517 panic!("expected tracked announce")
518 };
519 completion.finish(Ok(()));
520 receipt.wait().unwrap();
521 let mut packet = Box::pin(node.send_packet(&dest, b"async"));
522 assert!(packet.as_mut().now_or_never().is_none());
523 let Event::SendOutboundTracked {
524 completion, raw, ..
525 } = rx.try_recv().unwrap()
526 else {
527 panic!("expected tracked packet")
528 };
529 let hash = rns_core::types::PacketHash(
530 rns_core::packet::RawPacket::unpack(&raw)
531 .unwrap()
532 .packet_hash,
533 );
534 completion.finish(Ok(()));
535 assert_eq!(block_on(packet).unwrap(), hash);
536 assert!(
537 tx.link_send_pool().snapshot().is_empty(),
538 "non-Link sends must not affect per-Link counts"
539 );
540 drop(rx);
541 assert!(matches!(
542 node.try_send_packet(&dest, b"stopped"),
543 Err(LinkSendError::DriverStopped)
544 ));
545 assert_eq!(
546 block_on(node.announce(&announce_dest, &identity, None)),
547 Err(LinkSendError::DriverStopped)
548 );
549 }
550
551 #[test]
552 fn per_link_counts_cover_failure_shutdown_and_receipt_cancellation() {
553 let pool = SendPool::new(3);
554 let waiting = pool.track([1; 16], true);
555 let tracking = pool.track([2; 16], true);
556 let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
557 completion.admit();
558 assert_eq!(
559 pool.snapshot().get(&[1; 16]),
560 Some(&SendCounts {
561 pending: 0,
562 waiting: 1
563 })
564 );
565 assert_eq!(
566 pool.snapshot().get(&[2; 16]),
567 Some(&SendCounts {
568 pending: 1,
569 waiting: 0
570 })
571 );
572 drop(receipt);
573 assert_eq!(
574 pool.snapshot()[&[2; 16]].pending,
575 1,
576 "dropping a receipt does not cancel transmission"
577 );
578 completion.finish(Err(LinkSendError::WriteFailed("broken pipe".into())));
579 assert!(!pool.snapshot().contains_key(&[2; 16]));
580 drop(waiting);
581 assert!(pool.snapshot().is_empty());
582
583 let tracking = pool.track([4; 16], false);
584 let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
585 completion.admit();
586 drop(completion);
587 assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
588 assert!(pool.snapshot().is_empty());
589
590 let tracking = pool.track([3; 16], false);
591 let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
592 completion.admit();
593 let waiting = pool.track([3; 16], true);
594 pool.close();
595 assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
596 assert!(pool.snapshot().is_empty());
597 drop(waiting);
598 drop(completion);
599 assert!(pool.snapshot().is_empty());
600 }
601
602 #[test]
603 fn node_try_rejects_full_capacity_and_async_send_wakes_without_blocking() {
604 use crate::event::Event;
605 use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
606 struct WakeCount(AtomicUsize);
607 impl futures::task::ArcWake for WakeCount {
608 fn wake_by_ref(this: &Arc<Self>) {
609 this.0.fetch_add(1, Ordering::SeqCst);
610 }
611 }
612 let (tx, rx) = crate::event::channel_with_capacity(1);
613 let node = crate::RnsNode::from_parts(
614 tx.clone(),
615 std::thread::spawn(|| {}),
616 None,
617 Arc::new(AtomicU64::new(1000)),
618 Arc::new(std::sync::atomic::AtomicBool::new(true)),
619 );
620 let receipt = node.try_send_on_link([1; 16], vec![1], 0).unwrap();
621 assert_eq!(
622 tx.link_send_pool().snapshot()[&[1; 16]],
623 SendCounts {
624 pending: 1,
625 waiting: 0
626 }
627 );
628 let first = match rx.try_recv().unwrap() {
629 Event::SendLinkTracked { completion, .. } => completion,
630 _ => panic!("expected a tracked send"),
631 };
632 assert!(matches!(
633 node.try_send_on_link([1; 16], vec![2], 0),
634 Err(LinkSendError::QueueFull)
635 ));
636 let mut pending = Box::pin(node.send_on_link([1; 16], vec![2], 0));
637 let wakes = Arc::new(WakeCount(AtomicUsize::new(0)));
638 let waker = futures::task::waker(wakes.clone());
639 let mut cx = Context::from_waker(&waker);
640 assert!(pending.as_mut().poll(&mut cx).is_pending());
641 assert_eq!(
642 tx.link_send_pool().snapshot()[&[1; 16]],
643 SendCounts {
644 pending: 1,
645 waiting: 1
646 }
647 );
648 assert!(
649 rx.try_recv().is_err(),
650 "capacity wait must not enqueue prematurely"
651 );
652 first.finish(Ok(()));
653 receipt.wait().unwrap();
654 assert_eq!(
655 tx.link_send_pool().snapshot()[&[1; 16]],
656 SendCounts {
657 pending: 0,
658 waiting: 1
659 }
660 );
661 assert!(
662 wakes.0.load(Ordering::SeqCst) > 0,
663 "capacity release must wake the task"
664 );
665 assert!(pending.as_mut().poll(&mut cx).is_pending());
666 let second = match rx.try_recv().unwrap() {
667 Event::SendLinkTracked { completion, .. } => completion,
668 _ => panic!("expected a tracked send"),
669 };
670 assert_eq!(
671 tx.link_send_pool().snapshot()[&[1; 16]],
672 SendCounts {
673 pending: 1,
674 waiting: 0
675 }
676 );
677 second.finish(Ok(()));
678 assert!(tx.link_send_pool().snapshot().is_empty());
679 assert_eq!(pending.as_mut().poll(&mut cx), Poll::Ready(Ok(())));
680 drop(pending);
681
682 tx.try_send(Event::Tick).unwrap();
684 assert!(matches!(
685 node.try_send_on_link([1; 16], vec![3], 0),
686 Err(LinkSendError::QueueFull)
687 ));
688 let mut cancelled = Box::pin(node.send_on_link([1; 16], vec![3], 0));
689 assert!(cancelled.as_mut().poll(&mut cx).is_pending());
690 assert_eq!(
691 tx.link_send_pool().snapshot()[&[1; 16]],
692 SendCounts {
693 pending: 0,
694 waiting: 1
695 }
696 );
697 drop(cancelled);
698 assert!(tx.link_send_pool().snapshot().is_empty());
699 assert_eq!(tx.link_send_pool().in_flight(), 0);
700 assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
701 assert!(
702 rx.try_recv().is_err(),
703 "cancelled admission must not send later"
704 );
705 drop(rx);
706 node.shutdown();
707 }
708
709 #[test]
710 fn capacity_waits_for_transmission_not_receipt_creation() {
711 let pool = SendPool::new(1);
712 let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
713 assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
714 assert!((&mut receipt).now_or_never().is_none());
715 let mut waiting = Box::pin(pool.acquire());
716 assert!(waiting.as_mut().now_or_never().is_none());
717 completion.finish(Ok(()));
718 receipt.wait().unwrap();
719 let permit = block_on(waiting).unwrap();
720 assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
721 drop(permit);
722 assert!(pool.try_acquire().is_ok());
723 }
724
725 #[test]
726 fn shutdown_resolves_in_flight_receipts_and_capacity_waiters() {
727 let pool = SendPool::new(1);
728 let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
729 let mut waiting = Box::pin(pool.acquire());
730 assert!(waiting.as_mut().now_or_never().is_none());
731 pool.close();
732 assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
733 assert!(matches!(
734 block_on(waiting),
735 Err(LinkSendError::DriverStopped)
736 ));
737 completion.finish(Ok(()));
739 }
740
741 #[test]
742 fn dropping_receipt_does_not_release_an_in_flight_slot() {
743 let pool = SendPool::new(1);
744 let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
745 drop(receipt);
746 assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
747 drop(completion);
748 assert!(pool.try_acquire().is_ok());
749 }
750
751 #[test]
752 fn both_node_apis_report_the_same_invalid_link_error() {
753 struct Callbacks;
754 impl crate::Callbacks for Callbacks {
755 fn on_announce(&mut self, _: crate::AnnouncedIdentity) {}
756 fn on_path_updated(&mut self, _: crate::DestHash, _: u8) {}
757 fn on_local_delivery(&mut self, _: crate::DestHash, _: Vec<u8>, _: crate::PacketHash) {}
758 }
759 let node =
760 crate::RnsNode::start(crate::NodeConfig::default(), Box::new(Callbacks)).unwrap();
761 let expected = Err(LinkSendError::InvalidPacket(
762 crate::event::LinkDatagramError::LinkNotFound,
763 ));
764 assert_eq!(
765 block_on(node.send_on_link([0x42; 16], vec![1], 0)),
766 expected
767 );
768 assert_eq!(
769 node.try_send_on_link([0x42; 16], vec![1], 0)
770 .unwrap()
771 .wait(),
772 expected
773 );
774 let dest = crate::Destination::plain("confirmed", &["no_route"]);
775 assert_eq!(
776 block_on(node.send_packet(&dest, b"data")),
777 Err(LinkSendError::NoRoute)
778 );
779 assert_eq!(
780 node.try_send_packet(&dest, b"data").unwrap().wait(),
781 Err(LinkSendError::NoRoute)
782 );
783 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
784 let announce_dest = crate::Destination::single_in(
785 "confirmed",
786 &["announce"],
787 crate::IdentityHash(*identity.hash()),
788 );
789 assert_eq!(
790 block_on(node.announce(&announce_dest, &identity, None)),
791 Err(LinkSendError::NoRoute)
792 );
793 assert_eq!(
794 node.try_announce(&announce_dest, &identity, None)
795 .unwrap()
796 .wait(),
797 Err(LinkSendError::NoRoute)
798 );
799 let oversized = vec![0; rns_core::constants::MTU * 2];
800 assert_eq!(
801 block_on(node.send_packet(&dest, &oversized)),
802 Err(LinkSendError::PacketBuildFailed)
803 );
804 assert!(matches!(
805 node.try_send_packet(&dest, &oversized),
806 Err(LinkSendError::PacketBuildFailed)
807 ));
808 node.begin_drain(std::time::Duration::from_secs(30))
809 .unwrap();
810 node.drain_status().unwrap();
811 assert_eq!(
812 block_on(node.send_packet(&dest, b"data")),
813 Err(LinkSendError::Draining)
814 );
815 assert_eq!(
816 node.try_announce(&announce_dest, &identity, None)
817 .unwrap()
818 .wait(),
819 Err(LinkSendError::Draining)
820 );
821 node.shutdown();
822 }
823}