commonware_utils/channel/
reservation.rs1use super::mpsc::{
4 self, OwnedPermit,
5 error::{SendError, TrySendError},
6};
7use std::{
8 future::Future,
9 pin::Pin,
10 task::{Context, Poll},
11};
12
13type ReserveResult<T> = Result<OwnedPermit<T>, SendError<()>>;
15
16type ReserveFuture<T> = Pin<Box<dyn Future<Output = ReserveResult<T>> + Send>>;
18
19#[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 pub fn send(self) -> mpsc::Sender<T> {
29 self.permit.send(self.value)
30 }
31}
32
33#[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
70pub trait ReservationExt<T> {
72 #[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}