1use std::fmt;
38use std::ops::{Deref, DerefMut};
39#[cfg(feature = "stream")]
40use std::pin::Pin;
41use std::sync::Arc;
42#[cfg(feature = "stream")]
43use std::task::{Context, Poll};
44
45use tokio::sync::mpsc;
46use tokio::sync::{OwnedSemaphorePermit, Semaphore, TryAcquireError};
47
48const MAX_PERMITS: usize = u32::MAX as usize;
52
53pub trait Weigh {
60 fn weight(&self) -> usize;
62}
63
64#[cfg(feature = "weigh-std")]
68mod weigh_std {
69 use super::Weigh;
70
71 impl Weigh for Vec<u8> {
72 fn weight(&self) -> usize {
73 self.len()
74 }
75 }
76
77 impl Weigh for String {
78 fn weight(&self) -> usize {
79 self.len()
80 }
81 }
82
83 impl Weigh for Box<[u8]> {
84 fn weight(&self) -> usize {
85 self.len()
86 }
87 }
88}
89
90#[cfg(feature = "weigh-bytes")]
93mod weigh_bytes {
94 use super::Weigh;
95
96 impl Weigh for bytes::Bytes {
97 fn weight(&self) -> usize {
98 self.len()
99 }
100 }
101
102 impl Weigh for bytes::BytesMut {
103 fn weight(&self) -> usize {
104 self.len()
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
115pub enum Oversized {
116 #[default]
125 Allow,
126 Reject,
129 Drop,
132}
133
134pub enum SendError<T> {
136 Closed(T),
139 TooLarge(T),
142}
143
144impl<T> SendError<T> {
145 pub fn into_inner(self) -> T {
147 match self {
148 SendError::Closed(v) | SendError::TooLarge(v) => v,
149 }
150 }
151}
152
153impl<T> fmt::Debug for SendError<T> {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 SendError::Closed(_) => f.write_str("SendError::Closed(..)"),
157 SendError::TooLarge(_) => f.write_str("SendError::TooLarge(..)"),
158 }
159 }
160}
161
162impl<T> fmt::Display for SendError<T> {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 SendError::Closed(_) => f.write_str("channel closed: the receiver was dropped"),
166 SendError::TooLarge(_) => f.write_str("message weighs more than the channel budget"),
167 }
168 }
169}
170
171impl<T> std::error::Error for SendError<T> {}
172
173pub enum TrySendError<T> {
175 Full(T),
179 Closed(T),
182 TooLarge(T),
185}
186
187impl<T> TrySendError<T> {
188 pub fn into_inner(self) -> T {
190 match self {
191 TrySendError::Full(v) | TrySendError::Closed(v) | TrySendError::TooLarge(v) => v,
192 }
193 }
194}
195
196impl<T> fmt::Debug for TrySendError<T> {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match self {
199 TrySendError::Full(_) => f.write_str("TrySendError::Full(..)"),
200 TrySendError::Closed(_) => f.write_str("TrySendError::Closed(..)"),
201 TrySendError::TooLarge(_) => f.write_str("TrySendError::TooLarge(..)"),
202 }
203 }
204}
205
206impl<T> fmt::Display for TrySendError<T> {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 match self {
209 TrySendError::Full(_) => {
210 f.write_str("channel full: no room in the weight budget or count buffer")
211 }
212 TrySendError::Closed(_) => f.write_str("channel closed: the receiver was dropped"),
213 TrySendError::TooLarge(_) => f.write_str("message weighs more than the channel budget"),
214 }
215 }
216}
217
218impl<T> std::error::Error for TrySendError<T> {}
219
220type OversizedHook = Arc<dyn Fn(usize, usize) + Send + Sync>;
223
224#[derive(Clone)]
226pub struct Builder {
227 buffer: usize,
228 max_weight: usize,
229 min_weight: usize,
230 oversized: Oversized,
231 on_oversized: Option<OversizedHook>,
232}
233
234impl Builder {
235 pub fn new(buffer: usize, max_weight: usize) -> Self {
245 Self {
246 buffer,
247 max_weight,
248 min_weight: 1,
249 oversized: Oversized::default(),
250 on_oversized: None,
251 }
252 }
253
254 pub fn min_weight(mut self, min_weight: usize) -> Self {
259 self.min_weight = min_weight;
260 self
261 }
262
263 pub fn oversized(mut self, policy: Oversized) -> Self {
266 self.oversized = policy;
267 self
268 }
269
270 pub fn on_oversized<F>(mut self, hook: F) -> Self
273 where
274 F: Fn(usize, usize) + Send + Sync + 'static,
275 {
276 self.on_oversized = Some(Arc::new(hook));
277 self
278 }
279
280 pub fn build<T>(self) -> (WeightedSender<T>, WeightedReceiver<T>) {
286 assert!(self.buffer > 0, "buffer must be > 0");
287 assert!(self.max_weight > 0, "max_weight must be > 0");
288 assert!(
291 self.max_weight <= MAX_PERMITS,
292 "max_weight must be <= u32::MAX (about 4 GiB)"
293 );
294
295 let budget = Arc::new(Semaphore::new(self.max_weight));
296 let (tx, rx) = mpsc::channel(self.buffer);
297
298 let sender = WeightedSender {
299 tx,
300 budget: Arc::clone(&budget),
301 max_weight: self.max_weight,
302 min_weight: self.min_weight,
303 oversized: self.oversized,
304 on_oversized: self.on_oversized,
305 };
306 let receiver = WeightedReceiver { rx, budget };
307 (sender, receiver)
308 }
309}
310
311impl fmt::Debug for Builder {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 f.debug_struct("Builder")
314 .field("buffer", &self.buffer)
315 .field("max_weight", &self.max_weight)
316 .field("min_weight", &self.min_weight)
317 .field("oversized", &self.oversized)
318 .finish_non_exhaustive()
319 }
320}
321
322pub fn channel<T>(buffer: usize, max_weight: usize) -> (WeightedSender<T>, WeightedReceiver<T>) {
331 Builder::new(buffer, max_weight).build()
332}
333
334pub struct WeightedSender<T> {
336 tx: mpsc::Sender<Lease<T>>,
337 budget: Arc<Semaphore>,
338 max_weight: usize,
339 min_weight: usize,
340 oversized: Oversized,
341 on_oversized: Option<OversizedHook>,
342}
343
344impl<T> Clone for WeightedSender<T> {
346 fn clone(&self) -> Self {
347 Self {
348 tx: self.tx.clone(),
349 budget: Arc::clone(&self.budget),
350 max_weight: self.max_weight,
351 min_weight: self.min_weight,
352 oversized: self.oversized,
353 on_oversized: self.on_oversized.clone(),
354 }
355 }
356}
357
358impl<T: Weigh> WeightedSender<T> {
359 pub async fn send(&self, value: T) -> Result<(), SendError<T>> {
371 let weight = value.weight().max(self.min_weight);
372
373 let reserve = if weight > self.max_weight {
374 if let Some(hook) = &self.on_oversized {
375 hook(weight, self.max_weight);
376 }
377 match self.oversized {
378 Oversized::Reject => return Err(SendError::TooLarge(value)),
379 Oversized::Drop => return Ok(()),
380 Oversized::Allow => self.max_weight,
383 }
384 } else {
385 weight
386 };
387
388 let permit = match Arc::clone(&self.budget)
391 .acquire_many_owned(reserve as u32)
392 .await
393 {
394 Ok(permit) => permit,
395 Err(_) => return Err(SendError::Closed(value)),
398 };
399
400 self.tx
401 .send(Lease {
402 value,
403 weight: reserve,
404 _permit: permit,
405 })
406 .await
407 .map_err(|e| SendError::Closed(e.0.value))
408 }
409
410 pub fn try_send(&self, value: T) -> Result<(), TrySendError<T>> {
426 let weight = value.weight().max(self.min_weight);
427
428 let reserve = if weight > self.max_weight {
429 if let Some(hook) = &self.on_oversized {
430 hook(weight, self.max_weight);
431 }
432 match self.oversized {
433 Oversized::Reject => return Err(TrySendError::TooLarge(value)),
434 Oversized::Drop => return Ok(()),
435 Oversized::Allow => self.max_weight,
436 }
437 } else {
438 weight
439 };
440
441 let permit = match Arc::clone(&self.budget).try_acquire_many_owned(reserve as u32) {
444 Ok(permit) => permit,
445 Err(TryAcquireError::NoPermits) => return Err(TrySendError::Full(value)),
446 Err(TryAcquireError::Closed) => return Err(TrySendError::Closed(value)),
449 };
450
451 self.tx
454 .try_send(Lease {
455 value,
456 weight: reserve,
457 _permit: permit,
458 })
459 .map_err(|e| match e {
460 mpsc::error::TrySendError::Full(lease) => TrySendError::Full(lease.into_inner()),
461 mpsc::error::TrySendError::Closed(lease) => {
462 TrySendError::Closed(lease.into_inner())
463 }
464 })
465 }
466
467 pub fn max_weight(&self) -> usize {
469 self.max_weight
470 }
471
472 pub fn available_weight(&self) -> usize {
474 self.budget.available_permits()
475 }
476}
477
478impl<T> fmt::Debug for WeightedSender<T> {
479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480 f.debug_struct("WeightedSender")
481 .field("max_weight", &self.max_weight)
482 .field("min_weight", &self.min_weight)
483 .field("oversized", &self.oversized)
484 .field("available_weight", &self.budget.available_permits())
485 .finish_non_exhaustive()
486 }
487}
488
489pub struct WeightedReceiver<T> {
494 rx: mpsc::Receiver<Lease<T>>,
495 budget: Arc<Semaphore>,
496}
497
498impl<T> WeightedReceiver<T> {
499 pub async fn recv(&mut self) -> Option<Lease<T>> {
506 self.rx.recv().await
507 }
508
509 pub fn close(&mut self) {
512 self.rx.close();
513 self.budget.close();
514 }
515}
516
517impl<T> Drop for WeightedReceiver<T> {
518 fn drop(&mut self) {
519 self.budget.close();
523 }
524}
525
526impl<T> fmt::Debug for WeightedReceiver<T> {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 f.debug_struct("WeightedReceiver")
529 .field("available_weight", &self.budget.available_permits())
530 .finish_non_exhaustive()
531 }
532}
533
534#[cfg(feature = "stream")]
550impl<T> futures_core::Stream for WeightedReceiver<T> {
551 type Item = Lease<T>;
552
553 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
554 self.rx.poll_recv(cx)
555 }
556}
557
558pub struct Lease<T> {
565 value: T,
566 weight: usize,
567 _permit: OwnedSemaphorePermit,
570}
571
572impl<T> Lease<T> {
573 pub fn into_inner(self) -> T {
575 self.value
576 }
577
578 pub fn weight(&self) -> usize {
581 self.weight
582 }
583}
584
585impl<T> Deref for Lease<T> {
586 type Target = T;
587 fn deref(&self) -> &T {
588 &self.value
589 }
590}
591
592impl<T> DerefMut for Lease<T> {
593 fn deref_mut(&mut self) -> &mut T {
594 &mut self.value
595 }
596}
597
598impl<T: fmt::Debug> fmt::Debug for Lease<T> {
599 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600 f.debug_struct("Lease")
601 .field("value", &self.value)
602 .field("weight", &self.weight)
603 .finish()
604 }
605}
606
607#[cfg(test)]
608mod tests {
609 use std::sync::atomic::{AtomicUsize, Ordering};
610 use std::time::Duration;
611
612 use tokio::time::timeout;
613
614 use super::*;
615
616 #[derive(Debug)]
617 struct Msg(Vec<u8>);
618
619 impl Weigh for Msg {
620 fn weight(&self) -> usize {
621 self.0.len()
622 }
623 }
624
625 fn msg(bytes: usize) -> Msg {
626 Msg(vec![0u8; bytes])
627 }
628
629 #[tokio::test]
630 async fn delivers_all_messages() {
631 let (tx, mut rx) = channel::<Msg>(8, 1024 * 1024);
632 let producer = tokio::spawn(async move {
635 for _ in 0..10 {
636 tx.send(msg(1000)).await.unwrap();
637 }
638 });
639
640 let mut got = 0;
641 while let Some(d) = rx.recv().await {
642 assert_eq!(d.0.len(), 1000);
643 got += 1;
644 }
645 producer.await.unwrap();
646 assert_eq!(got, 10);
647 }
648
649 #[tokio::test]
650 async fn bounds_by_weight_not_count() {
651 let (tx, mut rx) = channel::<Msg>(64, 1000);
654 tx.send(msg(600)).await.unwrap();
655 assert_eq!(tx.available_weight(), 400);
656
657 let tx2 = tx.clone();
658 let blocked = tokio::spawn(async move { tx2.send(msg(600)).await });
659
660 tokio::time::sleep(Duration::from_millis(50)).await;
663 assert!(!blocked.is_finished(), "send should be waiting for room");
664
665 let first = rx.recv().await.unwrap();
667 assert_eq!(first.weight(), 600);
668 drop(first);
669
670 timeout(Duration::from_millis(200), blocked)
671 .await
672 .expect("send should unblock once room frees")
673 .unwrap()
674 .unwrap();
675 }
676
677 #[tokio::test]
678 async fn holding_lease_keeps_budget_reserved() {
679 let (tx, mut rx) = channel::<Msg>(64, 1000);
680 tx.send(msg(400)).await.unwrap();
681 let held = rx.recv().await.unwrap();
682 assert_eq!(tx.available_weight(), 600);
685 drop(held);
686 assert_eq!(tx.available_weight(), 1000);
687 }
688
689 #[tokio::test]
690 async fn into_inner_frees_budget() {
691 let (tx, mut rx) = channel::<Msg>(64, 1000);
692 tx.send(msg(400)).await.unwrap();
693 let value = rx.recv().await.unwrap().into_inner();
694 assert_eq!(tx.available_weight(), 1000);
696 assert_eq!(value.0.len(), 400);
697 }
698
699 #[tokio::test]
700 async fn oversized_allow_delivers_whole_message() {
701 let (tx, mut rx) = channel::<Msg>(64, 1000);
702 tx.send(msg(5000)).await.unwrap();
704 assert_eq!(tx.available_weight(), 0);
705 let d = rx.recv().await.unwrap();
706 assert_eq!(d.weight(), 1000);
707 assert_eq!(d.0.len(), 5000, "no bytes dropped or truncated");
708 }
709
710 #[tokio::test]
711 async fn oversized_reject_returns_the_message() {
712 let (tx, mut rx) = Builder::new(64, 1000)
713 .oversized(Oversized::Reject)
714 .build::<Msg>();
715 let err = tx.send(msg(5000)).await.unwrap_err();
716 match err {
717 SendError::TooLarge(m) => assert_eq!(m.0.len(), 5000),
718 other => panic!("expected TooLarge, got {other:?}"),
719 }
720 assert_eq!(tx.available_weight(), 1000, "budget untouched on reject");
721 tx.send(msg(10)).await.unwrap();
723 assert!(rx.recv().await.is_some());
724 }
725
726 #[tokio::test]
727 async fn oversized_drop_silently_discards() {
728 let seen = Arc::new(AtomicUsize::new(0));
729 let seen2 = Arc::clone(&seen);
730 let (tx, mut rx) = Builder::new(64, 1000)
731 .oversized(Oversized::Drop)
732 .on_oversized(move |w, max| {
733 assert_eq!((w, max), (5000, 1000));
734 seen2.fetch_add(1, Ordering::SeqCst);
735 })
736 .build::<Msg>();
737
738 tx.send(msg(5000)).await.unwrap(); assert_eq!(seen.load(Ordering::SeqCst), 1);
740 assert_eq!(tx.available_weight(), 1000);
741
742 tx.send(msg(10)).await.unwrap();
743 let d = rx.recv().await.unwrap();
744 assert_eq!(d.0.len(), 10);
745 }
746
747 #[tokio::test]
748 async fn min_weight_floors_cheap_messages() {
749 let (tx, _rx) = Builder::new(64, 10).min_weight(2).build::<Msg>();
751 tx.send(msg(0)).await.unwrap();
752 assert_eq!(tx.available_weight(), 8);
753 }
754
755 #[test]
756 #[should_panic(expected = "max_weight must be <= u32::MAX")]
757 fn rejects_budget_over_u32() {
758 let _ = Builder::new(8, MAX_PERMITS + 1).build::<Msg>();
759 }
760
761 #[cfg(feature = "weigh-std")]
762 #[tokio::test]
763 async fn weighs_std_types() {
764 let (tx, _rx) = channel::<Box<[u8]>>(8, 1000);
766 tx.send(vec![0u8; 300].into_boxed_slice()).await.unwrap();
767 assert_eq!(tx.available_weight(), 700);
768 }
769
770 #[cfg(feature = "weigh-bytes")]
771 #[tokio::test]
772 async fn weighs_bytes() {
773 let (tx, mut rx) = channel::<bytes::Bytes>(8, 1000);
774 tx.send(bytes::Bytes::from(vec![0u8; 400])).await.unwrap();
775 assert_eq!(tx.available_weight(), 600);
776 let d = rx.recv().await.unwrap();
777 assert_eq!(d.len(), 400);
778 }
779
780 #[tokio::test]
781 async fn dropping_receiver_unblocks_waiting_sender() {
782 let (tx, rx) = channel::<Msg>(64, 1000);
783 tx.send(msg(1000)).await.unwrap(); let tx2 = tx.clone();
786 let blocked = tokio::spawn(async move { tx2.send(msg(1000)).await });
787
788 drop(rx);
790
791 let res = timeout(Duration::from_millis(200), blocked)
792 .await
793 .expect("send should wake when the receiver drops")
794 .unwrap();
795 assert!(matches!(res, Err(SendError::Closed(_))));
796 }
797
798 #[tokio::test]
799 async fn send_after_receiver_dropped_is_closed() {
800 let (tx, rx) = channel::<Msg>(8, 1000);
801 drop(rx);
802 let err = tx.send(msg(1)).await.unwrap_err();
803 assert!(matches!(err, SendError::Closed(_)));
804 assert_eq!(err.into_inner().0.len(), 1);
805 }
806
807 #[tokio::test]
808 async fn try_send_delivers_when_room() {
809 let (tx, mut rx) = channel::<Msg>(8, 1000);
810 tx.try_send(msg(400)).unwrap();
811 assert_eq!(tx.available_weight(), 600);
812 let d = rx.recv().await.unwrap();
813 assert_eq!(d.weight(), 400);
814 }
815
816 #[tokio::test]
817 async fn try_send_full_when_budget_exhausted() {
818 let (tx, _rx) = channel::<Msg>(64, 1000);
819 tx.try_send(msg(1000)).unwrap(); let err = tx.try_send(msg(1)).unwrap_err();
821 assert!(matches!(err, TrySendError::Full(_)));
822 assert_eq!(err.into_inner().0.len(), 1);
823 assert_eq!(tx.available_weight(), 0);
825 }
826
827 #[tokio::test]
828 async fn try_send_full_when_count_buffer_full() {
829 let (tx, _rx) = channel::<Msg>(1, 1000);
832 tx.try_send(msg(10)).unwrap();
833 let err = tx.try_send(msg(10)).unwrap_err();
834 assert!(matches!(err, TrySendError::Full(_)));
835 assert_eq!(tx.available_weight(), 990, "permit returned on buffer-full");
836 }
837
838 #[tokio::test]
839 async fn try_send_closed_after_receiver_dropped() {
840 let (tx, rx) = channel::<Msg>(8, 1000);
841 drop(rx);
842 let err = tx.try_send(msg(1)).unwrap_err();
843 assert!(matches!(err, TrySendError::Closed(_)));
844 assert_eq!(err.into_inner().0.len(), 1);
845 }
846
847 #[tokio::test]
848 async fn try_send_oversized_reject_returns_the_message() {
849 let (tx, _rx) = Builder::new(64, 1000)
850 .oversized(Oversized::Reject)
851 .build::<Msg>();
852 let err = tx.try_send(msg(5000)).unwrap_err();
853 assert!(matches!(err, TrySendError::TooLarge(_)));
854 assert_eq!(tx.available_weight(), 1000, "budget untouched on reject");
855 }
856
857 #[tokio::test]
858 async fn try_send_oversized_drop_silently_discards() {
859 let seen = Arc::new(AtomicUsize::new(0));
860 let seen2 = Arc::clone(&seen);
861 let (tx, _rx) = Builder::new(64, 1000)
862 .oversized(Oversized::Drop)
863 .on_oversized(move |_, _| {
864 seen2.fetch_add(1, Ordering::SeqCst);
865 })
866 .build::<Msg>();
867 tx.try_send(msg(5000)).unwrap(); assert_eq!(seen.load(Ordering::SeqCst), 1);
869 assert_eq!(tx.available_weight(), 1000);
870 }
871
872 #[tokio::test]
873 async fn try_send_oversized_allow_reserves_whole_budget() {
874 let (tx, mut rx) = channel::<Msg>(64, 1000);
875 tx.try_send(msg(5000)).unwrap();
876 assert_eq!(tx.available_weight(), 0);
877 let d = rx.recv().await.unwrap();
878 assert_eq!(d.weight(), 1000);
879 assert_eq!(d.0.len(), 5000, "no bytes dropped or truncated");
880 }
881
882 #[cfg(feature = "stream")]
883 #[tokio::test]
884 async fn stream_yields_lease_and_frees_budget_on_drop() {
885 use std::future::poll_fn;
886
887 use futures_core::Stream;
888
889 let (tx, mut rx) = channel::<Msg>(8, 1000);
890 tx.send(msg(400)).await.unwrap();
891
892 let lease = poll_fn(|cx| Pin::new(&mut rx).poll_next(cx)).await.unwrap();
894 assert_eq!(lease.weight(), 400);
895 assert_eq!(tx.available_weight(), 600);
897 drop(lease);
898 assert_eq!(tx.available_weight(), 1000);
899 }
900
901 #[cfg(feature = "stream")]
902 #[tokio::test]
903 async fn stream_ends_when_all_senders_dropped() {
904 use std::future::poll_fn;
905
906 use futures_core::Stream;
907
908 let (tx, mut rx) = channel::<Msg>(8, 1000);
909 tx.send(msg(10)).await.unwrap();
910 drop(tx);
911
912 assert!(
913 poll_fn(|cx| Pin::new(&mut rx).poll_next(cx))
914 .await
915 .is_some()
916 );
917 assert!(
918 poll_fn(|cx| Pin::new(&mut rx).poll_next(cx))
919 .await
920 .is_none(),
921 "stream ends after the last sender drops and the channel drains"
922 );
923 }
924}