Skip to main content

commonware_utils/channel/
reservation.rs

1//! Channel reservation helpers.
2
3use super::mpsc::{
4    self, OwnedPermit,
5    error::{SendError, TrySendError},
6};
7use std::{
8    future::Future,
9    pin::Pin,
10    task::{Context, Poll},
11};
12
13// The reserve future only reports channel closure; the message value is stored separately.
14type ReserveResult<T> = Result<OwnedPermit<T>, SendError<()>>;
15
16// Tokio's `reserve_owned` future is not nameable, so box it instead of exposing a future parameter.
17type ReserveFuture<T> = Pin<Box<dyn Future<Output = ReserveResult<T>> + Send>>;
18
19/// A reserved channel slot bundled with the value to send.
20#[must_use = "call send to deliver the reserved message"]
21pub struct Reserved<T> {
22    permit: OwnedPermit<T>,
23    value: T,
24}
25
26impl<T> Reserved<T> {
27    /// Sends the buffered value through the reserved slot.
28    pub fn send(self) -> mpsc::Sender<T> {
29        self.permit.send(self.value)
30    }
31}
32
33/// A future that waits for a channel slot and keeps ownership of the value.
34#[must_use = "await the reservation to acquire a channel slot"]
35pub struct Reservation<T> {
36    future: ReserveFuture<T>,
37    value: Option<T>,
38}
39
40impl<T> Reservation<T> {
41    fn new(future: impl Future<Output = ReserveResult<T>> + Send + 'static, value: T) -> Self {
42        Self {
43            future: Box::pin(future),
44            value: Some(value),
45        }
46    }
47}
48
49impl<T> Unpin for Reservation<T> {}
50
51impl<T> Future for Reservation<T> {
52    type Output = Result<Reserved<T>, SendError<T>>;
53
54    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
55        let permit = match self.future.as_mut().poll(cx) {
56            Poll::Pending => return Poll::Pending,
57            Poll::Ready(permit) => permit,
58        };
59        let value = self
60            .value
61            .take()
62            .expect("reservation polled after completion");
63        Poll::Ready(match permit {
64            Ok(permit) => Ok(Reserved { permit, value }),
65            Err(SendError(())) => Err(SendError(value)),
66        })
67    }
68}
69
70/// Extension trait for bounded channel sends that can reserve capacity.
71pub trait ReservationExt<T> {
72    /// Attempts to send immediately, reserving the message when the channel is full.
73    ///
74    /// Returns:
75    /// - `Ok(None)` when the value was sent immediately.
76    /// - `Ok(Some(_))` when the channel was full. Await the reservation and call
77    ///   [`Reserved::send`] to deliver the value.
78    /// - `Err(_)` when the receiver has been dropped.
79    #[must_use = "await and send any reservation"]
80    fn send_or_reserve(&self, value: T) -> Result<Option<Reservation<T>>, SendError<T>>
81    where
82        T: 'static;
83}
84
85impl<T: Send> ReservationExt<T> for mpsc::Sender<T> {
86    fn send_or_reserve(&self, value: T) -> Result<Option<Reservation<T>>, SendError<T>>
87    where
88        T: 'static,
89    {
90        match self.try_send(value) {
91            Ok(()) => Ok(None),
92            Err(TrySendError::Full(value)) => {
93                Ok(Some(Reservation::new(self.clone().reserve_owned(), value)))
94            }
95            Err(TrySendError::Closed(value)) => Err(SendError(value)),
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use commonware_macros::test_async;
104    use std::collections::BTreeMap;
105
106    #[test]
107    fn test_send_or_reserve_sends_immediately() {
108        let (sender, mut receiver) = mpsc::channel(1);
109        assert!(sender.send_or_reserve(1).unwrap().is_none());
110        assert_eq!(receiver.try_recv(), Ok(1));
111    }
112
113    #[test]
114    fn test_send_or_reserve_closed_returns_value() {
115        let (sender, receiver) = mpsc::channel(1);
116        drop(receiver);
117
118        match sender.send_or_reserve(1) {
119            Ok(_) => panic!("send should fail"),
120            Err(SendError(value)) => assert_eq!(value, 1),
121        }
122    }
123
124    #[test_async]
125    async fn test_send_or_reserve_waits_for_capacity() {
126        let (sender, mut receiver) = mpsc::channel(1);
127        sender.try_send(1).unwrap();
128
129        let reservation = sender
130            .send_or_reserve(2)
131            .unwrap()
132            .expect("channel should be full");
133        assert_eq!(receiver.recv().await, Some(1));
134        reservation.await.unwrap().send();
135        assert_eq!(receiver.recv().await, Some(2));
136    }
137
138    #[test_async]
139    async fn test_send_or_reserve_returns_value_when_closed_while_waiting() {
140        let (sender, receiver) = mpsc::channel(1);
141        sender.try_send(1).unwrap();
142
143        let reservation = sender
144            .send_or_reserve(2)
145            .unwrap()
146            .expect("channel should be full");
147        drop(receiver);
148
149        match reservation.await {
150            Ok(_) => panic!("reservation should fail"),
151            Err(SendError(value)) => assert_eq!(value, 2),
152        }
153    }
154
155    #[test_async]
156    async fn test_send_or_reserve_reservations_can_be_stored() {
157        let (sender, mut receiver) = mpsc::channel(1);
158        sender.try_send(0).unwrap();
159
160        let mut reservations = Vec::new();
161        reservations.push(
162            sender
163                .send_or_reserve(1)
164                .unwrap()
165                .expect("channel should be full"),
166        );
167
168        let mut reservation_map = BTreeMap::new();
169        reservation_map.insert(
170            "next",
171            sender
172                .send_or_reserve(2)
173                .unwrap()
174                .expect("channel should be full"),
175        );
176
177        assert_eq!(receiver.recv().await, Some(0));
178        reservations.pop().unwrap().await.unwrap().send();
179        assert_eq!(receiver.recv().await, Some(1));
180        reservation_map
181            .remove("next")
182            .unwrap()
183            .await
184            .unwrap()
185            .send();
186        assert_eq!(receiver.recv().await, Some(2));
187    }
188}