Skip to main content

rns_net/
link_send.rs

1//! Runtime-independent transmission completion and bounded admission.
2use 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/// A local send failure. Success never implies acknowledgement by the peer.
10#[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/// Await this receipt to learn whether all selected interfaces finished writing.
43/// Obtaining a receipt means admission only; it is not a transmission result.
44/// Dropping a receipt does not cancel an admitted send.
45#[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    /// Blocking counterpart for synchronous callers. Do not call from a driver callback.
59    pub fn wait(self) -> Result<(), LinkSendError> {
60        futures::executor::block_on(self)
61    }
62}
63
64/// A packet's hash is available immediately for proof tracking, but successful
65/// transmission is reported only when this receipt resolves. Failure may follow
66/// partial transmission; neither the hash nor success promises remote delivery.
67#[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    /// Blocking completion wait. Do not call from a driver callback.
77    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/// Internal writer completion token. Dropping it reports an interrupted send.
271#[doc(hidden)]
272pub struct Completion {
273    state: Arc<CompletionState>,
274    settled: bool,
275}
276
277struct CompletionData {
278    tx: oneshot::Sender<Result<(), LinkSendError>>,
279    tracking: Option<SendTracking>,
280    permit: Permit,
281    remaining: usize,
282    error: Option<LinkSendError>,
283}
284struct CompletionState(Mutex<Option<CompletionData>>);
285impl Drop for CompletionState {
286    fn drop(&mut self) {
287        // Clear counters before waking a receipt whose writer/event vanished.
288        self.finish(Err(LinkSendError::Interrupted));
289    }
290}
291impl CompletionState {
292    fn finish(&self, result: Result<(), LinkSendError>) {
293        let data = self.0.lock().unwrap().take();
294        if let Some(data) = data {
295            drop(data.tracking);
296            drop(data.permit);
297            let _ = data.tx.send(result);
298        }
299    }
300}
301
302impl Completion {
303    pub(crate) fn new(permit: Permit) -> (Self, LinkSendReceipt) {
304        Self::new_inner(permit, None)
305    }
306
307    pub(crate) fn new_tracked(permit: Permit, tracking: SendTracking) -> (Self, LinkSendReceipt) {
308        Self::new_inner(permit, Some(tracking))
309    }
310
311    fn new_inner(permit: Permit, tracking: Option<SendTracking>) -> (Self, LinkSendReceipt) {
312        let (tx, rx) = oneshot::channel();
313        let pool = permit.0.clone();
314        let completion = Arc::new(CompletionState(Mutex::new(Some(CompletionData {
315            tx,
316            tracking,
317            permit,
318            remaining: 1,
319            error: None,
320        }))));
321        {
322            let mut pool = pool.state.lock().unwrap();
323            pool.completions.retain(|w| w.strong_count() > 0);
324            if pool.closed {
325                drop(pool);
326                completion.finish(Err(LinkSendError::DriverStopped));
327            } else {
328                pool.completions.push(Arc::downgrade(&completion));
329            }
330        }
331        (
332            Self {
333                state: completion,
334                settled: false,
335            },
336            LinkSendReceipt(rx),
337        )
338    }
339    pub(crate) fn finish(mut self, result: Result<(), LinkSendError>) {
340        self.settle(result);
341    }
342
343    /// Each selected interface gets a branch. The permit and receipt remain
344    /// live until every branch settles, even if one branch fails early.
345    pub(crate) fn branch(&self) -> Self {
346        if let Some(data) = self.state.0.lock().unwrap().as_mut() {
347            data.remaining += 1;
348        }
349        Self {
350            state: self.state.clone(),
351            settled: false,
352        }
353    }
354
355    fn settle(&mut self, result: Result<(), LinkSendError>) {
356        self.settled = true;
357        let final_data = {
358            let mut guard = self.state.0.lock().unwrap();
359            let Some(data) = guard.as_mut() else { return };
360            if data.error.is_none() {
361                data.error = result.err();
362            }
363            data.remaining -= 1;
364            if data.remaining == 0 {
365                guard.take()
366            } else {
367                None
368            }
369        };
370        if let Some(data) = final_data {
371            drop(data.tracking);
372            drop(data.permit);
373            let _ = data.tx.send(data.error.map_or(Ok(()), Err));
374        }
375    }
376    pub(crate) fn is_finished(&self) -> bool {
377        self.state.0.lock().unwrap().is_none()
378    }
379
380    pub(crate) fn admit(&self) {
381        if let Some(data) = self.state.0.lock().unwrap().as_mut() {
382            if let Some(tracking) = data.tracking.as_mut() {
383                tracking.admit();
384            }
385        }
386    }
387}
388
389impl Drop for Completion {
390    fn drop(&mut self) {
391        if !self.settled {
392            self.settle(Err(LinkSendError::Interrupted));
393        }
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400    use futures::{executor::block_on, FutureExt};
401
402    #[test]
403    fn fanout_keeps_capacity_until_all_writes_settle() {
404        let pool = SendPool::new(1);
405        let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
406        let first = completion.branch();
407        let last = completion.branch();
408        completion.finish(Ok(()));
409        first.finish(Err(LinkSendError::WriteFailed("first failed".into())));
410        assert!((&mut receipt).now_or_never().is_none());
411        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
412        last.finish(Ok(()));
413        assert_eq!(
414            receipt.wait(),
415            Err(LinkSendError::WriteFailed("first failed".into()))
416        );
417        assert_eq!(pool.in_flight(), 0);
418
419        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
420        let child = completion.branch();
421        completion.finish(Ok(()));
422        drop(child);
423        assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
424        assert_eq!(pool.in_flight(), 0);
425
426        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
427        let child = completion.branch();
428        pool.close();
429        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
430        assert_eq!(pool.in_flight(), 0);
431        drop((completion, child));
432    }
433
434    #[test]
435    fn packet_and_announce_admission_is_bounded_and_runtime_independent() {
436        use crate::event::Event;
437        let (tx, rx) = crate::event::channel_with_capacity(1);
438        let node = crate::RnsNode::from_parts(
439            tx.clone(),
440            std::thread::spawn(|| {}),
441            None,
442            Arc::new(std::sync::atomic::AtomicU64::new(1000)),
443        );
444        let dest = crate::Destination::plain("confirmed", &["packet"]);
445        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
446        let announce_dest = crate::Destination::single_in(
447            "confirmed",
448            &["announce"],
449            crate::IdentityHash(*identity.hash()),
450        );
451        drop(node.announce(&announce_dest, &identity, None));
452        assert!(
453            rx.try_recv().is_err(),
454            "unpolled sends must have no side effects"
455        );
456        tx.send(Event::Tick).unwrap();
457        assert!(matches!(
458            node.try_announce(&announce_dest, &identity, None),
459            Err(LinkSendError::QueueFull)
460        ));
461        let mut cancelled = Box::pin(node.announce(&announce_dest, &identity, None));
462        assert!(cancelled.as_mut().now_or_never().is_none());
463        drop(cancelled);
464        assert_eq!(tx.link_send_pool().in_flight(), 0);
465        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
466        assert!(
467            rx.try_recv().is_err(),
468            "cancelled admission must not update replay state"
469        );
470        let receipt = node.try_send_packet(&dest, b"first").unwrap();
471        let hash = receipt.packet_hash();
472        let Event::SendOutboundTracked {
473            completion: first, ..
474        } = rx.try_recv().unwrap()
475        else {
476            panic!("expected tracked packet")
477        };
478        assert!(matches!(
479            node.try_announce(&announce_dest, &identity, None),
480            Err(LinkSendError::QueueFull)
481        ));
482        let mut announce = Box::pin(node.announce(&announce_dest, &identity, None));
483        assert!(announce.as_mut().now_or_never().is_none());
484        assert!(rx.try_recv().is_err());
485        first.finish(Ok(()));
486        assert_eq!(receipt.wait().unwrap(), hash);
487        assert!(announce.as_mut().now_or_never().is_none());
488        let Event::SendOutboundTracked {
489            completion, replay, ..
490        } = rx.try_recv().unwrap()
491        else {
492            panic!("expected tracked announce")
493        };
494        assert_eq!(replay.unwrap().dest_hash, announce_dest.hash.0);
495        assert!(matches!(
496            node.try_send_packet(&dest, b"full"),
497            Err(LinkSendError::QueueFull)
498        ));
499        completion.finish(Ok(()));
500        block_on(announce).unwrap();
501        let receipt = node.try_announce(&announce_dest, &identity, None).unwrap();
502        let Event::SendOutboundTracked { completion, .. } = rx.try_recv().unwrap() else {
503            panic!("expected tracked announce")
504        };
505        completion.finish(Ok(()));
506        receipt.wait().unwrap();
507        let mut packet = Box::pin(node.send_packet(&dest, b"async"));
508        assert!(packet.as_mut().now_or_never().is_none());
509        let Event::SendOutboundTracked {
510            completion, raw, ..
511        } = rx.try_recv().unwrap()
512        else {
513            panic!("expected tracked packet")
514        };
515        let hash = rns_core::types::PacketHash(
516            rns_core::packet::RawPacket::unpack(&raw)
517                .unwrap()
518                .packet_hash,
519        );
520        completion.finish(Ok(()));
521        assert_eq!(block_on(packet).unwrap(), hash);
522        assert!(
523            tx.link_send_pool().snapshot().is_empty(),
524            "non-Link sends must not affect per-Link counts"
525        );
526        drop(rx);
527        assert!(matches!(
528            node.try_send_packet(&dest, b"stopped"),
529            Err(LinkSendError::DriverStopped)
530        ));
531        assert_eq!(
532            block_on(node.announce(&announce_dest, &identity, None)),
533            Err(LinkSendError::DriverStopped)
534        );
535    }
536
537    #[test]
538    fn per_link_counts_cover_failure_shutdown_and_receipt_cancellation() {
539        let pool = SendPool::new(3);
540        let waiting = pool.track([1; 16], true);
541        let tracking = pool.track([2; 16], true);
542        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
543        completion.admit();
544        assert_eq!(
545            pool.snapshot().get(&[1; 16]),
546            Some(&SendCounts {
547                pending: 0,
548                waiting: 1
549            })
550        );
551        assert_eq!(
552            pool.snapshot().get(&[2; 16]),
553            Some(&SendCounts {
554                pending: 1,
555                waiting: 0
556            })
557        );
558        drop(receipt);
559        assert_eq!(
560            pool.snapshot()[&[2; 16]].pending,
561            1,
562            "dropping a receipt does not cancel transmission"
563        );
564        completion.finish(Err(LinkSendError::WriteFailed("broken pipe".into())));
565        assert!(!pool.snapshot().contains_key(&[2; 16]));
566        drop(waiting);
567        assert!(pool.snapshot().is_empty());
568
569        let tracking = pool.track([4; 16], false);
570        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
571        completion.admit();
572        drop(completion);
573        assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
574        assert!(pool.snapshot().is_empty());
575
576        let tracking = pool.track([3; 16], false);
577        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
578        completion.admit();
579        let waiting = pool.track([3; 16], true);
580        pool.close();
581        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
582        assert!(pool.snapshot().is_empty());
583        drop(waiting);
584        drop(completion);
585        assert!(pool.snapshot().is_empty());
586    }
587
588    #[test]
589    fn node_try_rejects_full_capacity_and_async_send_wakes_without_blocking() {
590        use crate::event::Event;
591        use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
592        struct WakeCount(AtomicUsize);
593        impl futures::task::ArcWake for WakeCount {
594            fn wake_by_ref(this: &Arc<Self>) {
595                this.0.fetch_add(1, Ordering::SeqCst);
596            }
597        }
598        let (tx, rx) = crate::event::channel_with_capacity(1);
599        let node = crate::RnsNode::from_parts(
600            tx.clone(),
601            std::thread::spawn(|| {}),
602            None,
603            Arc::new(AtomicU64::new(1000)),
604        );
605        let receipt = node.try_send_on_link([1; 16], vec![1], 0).unwrap();
606        assert_eq!(
607            tx.link_send_pool().snapshot()[&[1; 16]],
608            SendCounts {
609                pending: 1,
610                waiting: 0
611            }
612        );
613        let first = match rx.try_recv().unwrap() {
614            Event::SendLinkTracked { completion, .. } => completion,
615            _ => panic!("expected a tracked send"),
616        };
617        assert!(matches!(
618            node.try_send_on_link([1; 16], vec![2], 0),
619            Err(LinkSendError::QueueFull)
620        ));
621        let mut pending = Box::pin(node.send_on_link([1; 16], vec![2], 0));
622        let wakes = Arc::new(WakeCount(AtomicUsize::new(0)));
623        let waker = futures::task::waker(wakes.clone());
624        let mut cx = Context::from_waker(&waker);
625        assert!(pending.as_mut().poll(&mut cx).is_pending());
626        assert_eq!(
627            tx.link_send_pool().snapshot()[&[1; 16]],
628            SendCounts {
629                pending: 1,
630                waiting: 1
631            }
632        );
633        assert!(
634            rx.try_recv().is_err(),
635            "capacity wait must not enqueue prematurely"
636        );
637        first.finish(Ok(()));
638        receipt.wait().unwrap();
639        assert_eq!(
640            tx.link_send_pool().snapshot()[&[1; 16]],
641            SendCounts {
642                pending: 0,
643                waiting: 1
644            }
645        );
646        assert!(
647            wakes.0.load(Ordering::SeqCst) > 0,
648            "capacity release must wake the task"
649        );
650        assert!(pending.as_mut().poll(&mut cx).is_pending());
651        let second = match rx.try_recv().unwrap() {
652            Event::SendLinkTracked { completion, .. } => completion,
653            _ => panic!("expected a tracked send"),
654        };
655        assert_eq!(
656            tx.link_send_pool().snapshot()[&[1; 16]],
657            SendCounts {
658                pending: 1,
659                waiting: 0
660            }
661        );
662        second.finish(Ok(()));
663        assert!(tx.link_send_pool().snapshot().is_empty());
664        assert_eq!(pending.as_mut().poll(&mut cx), Poll::Ready(Ok(())));
665        drop(pending);
666
667        // A full *driver* event queue also makes try return immediately.
668        tx.try_send(Event::Tick).unwrap();
669        assert!(matches!(
670            node.try_send_on_link([1; 16], vec![3], 0),
671            Err(LinkSendError::QueueFull)
672        ));
673        let mut cancelled = Box::pin(node.send_on_link([1; 16], vec![3], 0));
674        assert!(cancelled.as_mut().poll(&mut cx).is_pending());
675        assert_eq!(
676            tx.link_send_pool().snapshot()[&[1; 16]],
677            SendCounts {
678                pending: 0,
679                waiting: 1
680            }
681        );
682        drop(cancelled);
683        assert!(tx.link_send_pool().snapshot().is_empty());
684        assert_eq!(tx.link_send_pool().in_flight(), 0);
685        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
686        assert!(
687            rx.try_recv().is_err(),
688            "cancelled admission must not send later"
689        );
690        drop(rx);
691        node.shutdown();
692    }
693
694    #[test]
695    fn capacity_waits_for_transmission_not_receipt_creation() {
696        let pool = SendPool::new(1);
697        let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
698        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
699        assert!((&mut receipt).now_or_never().is_none());
700        let mut waiting = Box::pin(pool.acquire());
701        assert!(waiting.as_mut().now_or_never().is_none());
702        completion.finish(Ok(()));
703        receipt.wait().unwrap();
704        let permit = block_on(waiting).unwrap();
705        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
706        drop(permit);
707        assert!(pool.try_acquire().is_ok());
708    }
709
710    #[test]
711    fn shutdown_resolves_in_flight_receipts_and_capacity_waiters() {
712        let pool = SendPool::new(1);
713        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
714        let mut waiting = Box::pin(pool.acquire());
715        assert!(waiting.as_mut().now_or_never().is_none());
716        pool.close();
717        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
718        assert!(matches!(
719            block_on(waiting),
720            Err(LinkSendError::DriverStopped)
721        ));
722        // A delayed worker cannot overwrite the shutdown result.
723        completion.finish(Ok(()));
724    }
725
726    #[test]
727    fn dropping_receipt_does_not_release_an_in_flight_slot() {
728        let pool = SendPool::new(1);
729        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
730        drop(receipt);
731        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
732        drop(completion);
733        assert!(pool.try_acquire().is_ok());
734    }
735
736    #[test]
737    fn both_node_apis_report_the_same_invalid_link_error() {
738        struct Callbacks;
739        impl crate::Callbacks for Callbacks {
740            fn on_announce(&mut self, _: crate::AnnouncedIdentity) {}
741            fn on_path_updated(&mut self, _: crate::DestHash, _: u8) {}
742            fn on_local_delivery(&mut self, _: crate::DestHash, _: Vec<u8>, _: crate::PacketHash) {}
743        }
744        let node =
745            crate::RnsNode::start(crate::NodeConfig::default(), Box::new(Callbacks)).unwrap();
746        let expected = Err(LinkSendError::InvalidPacket(
747            crate::event::LinkDatagramError::LinkNotFound,
748        ));
749        assert_eq!(
750            block_on(node.send_on_link([0x42; 16], vec![1], 0)),
751            expected
752        );
753        assert_eq!(
754            node.try_send_on_link([0x42; 16], vec![1], 0)
755                .unwrap()
756                .wait(),
757            expected
758        );
759        let dest = crate::Destination::plain("confirmed", &["no_route"]);
760        assert_eq!(
761            block_on(node.send_packet(&dest, b"data")),
762            Err(LinkSendError::NoRoute)
763        );
764        assert_eq!(
765            node.try_send_packet(&dest, b"data").unwrap().wait(),
766            Err(LinkSendError::NoRoute)
767        );
768        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
769        let announce_dest = crate::Destination::single_in(
770            "confirmed",
771            &["announce"],
772            crate::IdentityHash(*identity.hash()),
773        );
774        assert_eq!(
775            block_on(node.announce(&announce_dest, &identity, None)),
776            Err(LinkSendError::NoRoute)
777        );
778        assert_eq!(
779            node.try_announce(&announce_dest, &identity, None)
780                .unwrap()
781                .wait(),
782            Err(LinkSendError::NoRoute)
783        );
784        let oversized = vec![0; rns_core::constants::MTU * 2];
785        assert_eq!(
786            block_on(node.send_packet(&dest, &oversized)),
787            Err(LinkSendError::PacketBuildFailed)
788        );
789        assert!(matches!(
790            node.try_send_packet(&dest, &oversized),
791            Err(LinkSendError::PacketBuildFailed)
792        ));
793        node.begin_drain(std::time::Duration::from_secs(30))
794            .unwrap();
795        node.drain_status().unwrap();
796        assert_eq!(
797            block_on(node.send_packet(&dest, b"data")),
798            Err(LinkSendError::Draining)
799        );
800        assert_eq!(
801            node.try_announce(&announce_dest, &identity, None)
802                .unwrap()
803                .wait(),
804            Err(LinkSendError::Draining)
805        );
806        node.shutdown();
807    }
808}