1use std::sync::Arc;
8use std::sync::atomic::{AtomicU64, Ordering};
9use tokio::sync::{Semaphore, mpsc};
10
11pub struct Envelope<T> {
12 pub item: T,
13 _bytes: Option<tokio::sync::OwnedSemaphorePermit>,
14}
15
16impl<T> Envelope<T> {
17 pub fn into_inner(self) -> T {
18 self.item
19 }
20}
21
22#[derive(Clone)]
23pub struct Sender<T> {
24 tx: mpsc::Sender<Envelope<T>>,
25 bytes: Arc<Semaphore>,
26 max_bytes: usize,
27 inflight: Arc<AtomicU64>,
28}
29
30pub struct Receiver<T> {
31 rx: mpsc::Receiver<Envelope<T>>,
32 inflight: Arc<AtomicU64>,
33}
34
35pub fn bounded<T>(capacity: usize, max_bytes: usize) -> (Sender<T>, Receiver<T>) {
36 let (tx, rx) = mpsc::channel(capacity);
37 let inflight = Arc::new(AtomicU64::new(0));
38 (
39 Sender {
40 tx,
41 bytes: Arc::new(Semaphore::new(max_bytes)),
42 max_bytes,
43 inflight: inflight.clone(),
44 },
45 Receiver { rx, inflight },
46 )
47}
48
49#[derive(Debug, thiserror::Error)]
50#[error("channel closed")]
51pub struct Closed;
52
53impl<T> Sender<T> {
54 pub async fn reserve(&self, bytes: usize) -> Result<Permit<'_, T>, Closed> {
58 let want = bytes.min(self.max_bytes).max(1) as u32;
59 let slot = self.tx.reserve().await.map_err(|_| Closed)?;
60 let permit = self
61 .bytes
62 .clone()
63 .acquire_many_owned(want)
64 .await
65 .map_err(|_| Closed)?;
66 self.inflight.fetch_add(1, Ordering::Relaxed);
67 Ok(Permit {
68 slot: Some(slot),
69 permit: Some(permit),
70 inflight: &self.inflight,
71 })
72 }
73
74 pub fn inflight_items(&self) -> u64 {
75 self.inflight.load(Ordering::Relaxed)
76 }
77
78 pub fn available_bytes(&self) -> usize {
79 self.bytes.available_permits()
80 }
81}
82
83pub struct Permit<'a, T> {
84 slot: Option<mpsc::Permit<'a, Envelope<T>>>,
85 permit: Option<tokio::sync::OwnedSemaphorePermit>,
86 inflight: &'a AtomicU64,
87}
88
89impl<T> Permit<'_, T> {
90 pub fn send(mut self, item: T) {
91 let bytes = self.permit.take();
92 if let Some(slot) = self.slot.take() {
93 slot.send(Envelope {
94 item,
95 _bytes: bytes,
96 });
97 }
98 }
99}
100
101impl<T> Drop for Permit<'_, T> {
102 fn drop(&mut self) {
103 if self.permit.is_some() {
104 self.inflight.fetch_sub(1, Ordering::Relaxed);
106 }
107 }
108}
109
110impl<T> Receiver<T> {
111 pub async fn recv(&mut self) -> Option<Envelope<T>> {
113 let e = self.rx.recv().await?;
114 self.inflight.fetch_sub(1, Ordering::Relaxed);
115 Some(e)
116 }
117
118 pub fn close(&mut self) {
119 self.rx.close();
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use std::time::Duration;
127
128 #[tokio::test]
129 async fn blocks_when_bytes_exhausted_until_consumer_drains() {
130 let (tx, mut rx) = bounded::<Vec<u8>>(100, 1000);
131 tx.reserve(600).await.unwrap().send(vec![0; 600]);
132 tx.reserve(300).await.unwrap().send(vec![0; 300]);
133 assert_eq!(tx.available_bytes(), 100);
134 assert_eq!(tx.inflight_items(), 2);
135
136 let blocked = tokio::time::timeout(Duration::from_millis(50), tx.reserve(200)).await;
137 assert!(blocked.is_err(), "third item should block on byte budget");
138
139 let first = rx.recv().await.unwrap();
140 assert_eq!(first.item.len(), 600);
141 drop(first);
142 let p = tokio::time::timeout(Duration::from_millis(500), tx.reserve(200)).await;
143 assert!(p.is_ok(), "should unblock after consumer releases bytes");
144 }
145
146 #[tokio::test]
147 async fn blocks_on_item_capacity() {
148 let (tx, mut rx) = bounded::<u8>(2, 1 << 20);
149 tx.reserve(1).await.unwrap().send(1);
150 tx.reserve(1).await.unwrap().send(2);
151 let blocked = tokio::time::timeout(Duration::from_millis(50), tx.reserve(1)).await;
152 assert!(blocked.is_err());
153 assert_eq!(rx.recv().await.unwrap().into_inner(), 1);
154 assert!(
155 tokio::time::timeout(Duration::from_millis(500), tx.reserve(1))
156 .await
157 .is_ok()
158 );
159 }
160
161 #[tokio::test]
162 async fn oversized_item_is_admitted_alone() {
163 let (tx, mut rx) = bounded::<Vec<u8>>(10, 100);
164 let p = tokio::time::timeout(Duration::from_millis(200), tx.reserve(10_000)).await;
165 assert!(p.is_ok(), "oversized item must not deadlock");
166 p.unwrap().unwrap().send(vec![1]);
167 assert_eq!(tx.available_bytes(), 0);
168 drop(rx.recv().await.unwrap());
169 assert_eq!(tx.available_bytes(), 100);
170 }
171
172 #[tokio::test]
173 async fn dropped_permit_releases_everything() {
174 let (tx, _rx) = bounded::<u8>(1, 10);
175 {
176 let _p = tx.reserve(10).await.unwrap();
177 assert_eq!(tx.inflight_items(), 1);
178 }
179 assert_eq!(tx.inflight_items(), 0);
180 assert_eq!(tx.available_bytes(), 10);
181 assert!(
182 tokio::time::timeout(Duration::from_millis(100), tx.reserve(10))
183 .await
184 .is_ok()
185 );
186 }
187
188 #[tokio::test]
189 async fn recv_returns_none_after_senders_drop() {
190 let (tx, mut rx) = bounded::<u8>(1, 10);
191 tx.reserve(1).await.unwrap().send(5);
192 drop(tx);
193 assert_eq!(rx.recv().await.unwrap().into_inner(), 5);
194 assert!(rx.recv().await.is_none());
195 }
196}