1use std::task::Poll;
11
12use crate::{Error, Result, track};
13
14#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct Rate(u64);
26
27impl Rate {
28 pub const ZERO: Self = Self(0);
30
31 pub const fn from_bps(bps: u64) -> Self {
33 Self(bps)
34 }
35
36 pub const fn from_kbps(kbps: u64) -> Self {
38 Self(kbps.saturating_mul(1_000))
39 }
40
41 pub const fn from_mbps(mbps: u64) -> Self {
43 Self(mbps.saturating_mul(1_000_000))
44 }
45
46 pub const fn as_bps(self) -> u64 {
48 self.0
49 }
50
51 pub fn scaled(self, factor: f64) -> Self {
56 let scaled = self.0 as f64 * factor.max(0.0);
57 if scaled >= u64::MAX as f64 {
58 Self(u64::MAX)
59 } else {
60 Self(scaled as u64)
61 }
62 }
63
64 pub const fn abs_diff(self, other: Self) -> Self {
66 Self(self.0.abs_diff(other.0))
67 }
68}
69
70#[derive(Default)]
71struct State {
72 bitrate: Option<Rate>,
73 abort: Option<Error>,
74}
75
76#[derive(Clone)]
78pub struct Producer {
79 state: kio::Producer<State>,
80}
81
82impl Producer {
83 pub fn new() -> Self {
85 Self {
86 state: kio::Producer::default(),
87 }
88 }
89
90 pub fn set(&self, bitrate: Option<Rate>) -> Result<()> {
92 let mut state = self.modify()?;
93 if state.bitrate != bitrate {
94 state.bitrate = bitrate;
95 }
96 Ok(())
97 }
98
99 pub fn consume(&self) -> Consumer {
101 Consumer {
102 inner: Inner::Whole(self.state.consume()),
103 last: None,
104 }
105 }
106
107 pub(crate) fn weak(&self) -> Weak {
109 Weak(self.state.weak())
110 }
111
112 pub fn abort(&self, err: Error) -> Result<()> {
114 let mut state = self.modify()?;
115 state.abort = Some(err);
116 state.close();
117 Ok(())
118 }
119
120 pub async fn closed(&self) -> Error {
122 self.state.closed().await;
123 self.close_error()
124 }
125
126 pub async fn unused(&self) -> Result<()> {
128 kio::wait(|waiter| self.poll_unused(waiter)).await
129 }
130
131 pub fn poll_unused(&self, waiter: &kio::Waiter) -> Poll<Result<()>> {
133 self.state.poll_unused(waiter).map(|used| match used {
134 Some(()) => Ok(()),
135 None => Err(self.close_error()),
136 })
137 }
138
139 pub fn is_used(&self) -> bool {
141 self.state.is_used()
142 }
143
144 pub async fn used(&self) -> Result<()> {
146 kio::wait(|waiter| self.poll_used(waiter)).await
147 }
148
149 pub fn poll_used(&self, waiter: &kio::Waiter) -> Poll<Result<()>> {
151 self.state.poll_used(waiter).map(|used| match used {
152 Some(()) => Ok(()),
153 None => Err(self.close_error()),
154 })
155 }
156
157 fn modify(&self) -> Result<kio::Mut<'_, State>> {
158 self.state
159 .write()
160 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
161 }
162
163 fn close_error(&self) -> Error {
165 self.state.read().abort.clone().unwrap_or(Error::Dropped)
166 }
167}
168
169impl Default for Producer {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175#[derive(Clone)]
177pub(crate) struct Weak(kio::ProducerWeak<State>);
178
179impl Weak {
180 pub(crate) fn consume(&self) -> Consumer {
182 Consumer {
183 inner: Inner::Whole(self.0.consume()),
184 last: None,
185 }
186 }
187}
188
189#[derive(Clone)]
203pub struct Allocator {
204 estimate: Consumer,
205 registry: kio::Producer<Registry>,
206}
207
208impl Allocator {
209 pub fn new(estimate: Consumer) -> Self {
212 Self {
213 estimate,
214 registry: kio::Producer::default(),
215 }
216 }
217
218 pub fn unlimited() -> Self {
225 Self {
226 estimate: Consumer {
227 inner: Inner::Unavailable,
228 last: None,
229 },
230 registry: kio::Producer::default(),
231 }
232 }
233
234 pub fn reserve(&self, track: &track::Demand, max: Rate) -> Reservation {
268 let priority = track.priority();
271 let demand = track.clone();
272
273 let id = {
274 let Ok(mut registry) = self.registry.write() else {
277 unreachable!("the allocator holds its own registry producer")
278 };
279 registry.entries.retain(|entry| !entry.demand.is_closed());
282
283 let id = registry.next_id;
284 registry.next_id += 1;
285 registry.entries.push(Entry {
286 id,
287 demand,
288 priority,
289 max,
290 });
291 id
292 };
293
294 Reservation {
295 share: Share {
296 estimate: self.estimate.clone(),
297 registry: self.registry.consume(),
298 id,
299 },
300 registry: self.registry.downgrade(),
301 }
302 }
303}
304
305#[must_use = "a dropped Reservation is released, so the sender claims nothing and its siblings take the room"]
312pub struct Reservation {
313 share: Share,
314 registry: kio::Weak<Registry>,
317}
318
319impl Reservation {
320 pub fn peek(&self) -> Option<Rate> {
325 self.share.grant()
326 }
327
328 pub fn consumer(&self) -> Consumer {
333 Consumer {
334 inner: Inner::Share(Box::new(self.share.clone())),
335 last: None,
336 }
337 }
338
339 pub fn update(&self, max: Rate) {
350 let Some(registry) = self.registry.upgrade() else {
351 return;
352 };
353 let Ok(mut registry) = registry.write() else {
354 return;
355 };
356 if let Some(entry) = registry.entries.iter_mut().find(|entry| entry.id == self.share.id) {
357 entry.max = max;
358 }
359 }
360}
361
362impl Drop for Reservation {
363 fn drop(&mut self) {
364 let Some(registry) = self.registry.upgrade() else {
365 return;
366 };
367 let Ok(mut registry) = registry.write() else {
368 return;
369 };
370 registry.entries.retain(|entry| entry.id != self.share.id);
371 }
372}
373
374impl Default for Allocator {
377 fn default() -> Self {
378 Self::unlimited()
379 }
380}
381
382impl std::fmt::Debug for Allocator {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 f.debug_struct("Allocator")
387 .field("registered", &self.registry.read().entries.len())
388 .finish()
389 }
390}
391
392#[derive(Default)]
394struct Registry {
395 entries: Vec<Entry>,
396 next_id: u64,
397}
398
399struct Entry {
401 id: u64,
402 demand: track::Demand,
403 priority: u8,
404 max: Rate,
405}
406
407#[derive(Clone)]
409struct Share {
410 estimate: Consumer,
412 registry: kio::Consumer<Registry>,
413 id: u64,
414}
415
416#[derive(Copy, Clone, Debug, Eq, PartialEq)]
419struct Want {
420 id: u64,
421 priority: u8,
422 max: Rate,
423}
424
425impl Share {
426 fn grant(&self) -> Option<Rate> {
428 let estimate = self.estimate.peek()?;
429 let wants: Vec<Want> = self
430 .claims()
431 .into_iter()
432 .filter(|(_, demand)| demand.is_used())
433 .map(|(want, _)| want)
434 .collect();
435 allocate(estimate, &wants, self.id)
436 }
437
438 fn poll_grant(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Rate>>> {
441 loop {
448 match self.estimate.poll_changed(waiter) {
449 Poll::Ready(Ok(_)) => continue,
452 Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
453 Poll::Pending => break,
454 }
455 }
456
457 if let Poll::Ready(Err(_)) = self.registry.poll(waiter, |_| Poll::<()>::Pending) {
460 return Poll::Ready(Err(Error::Dropped));
461 }
462
463 let wants: Vec<Want> = self
464 .claims()
465 .into_iter()
466 .filter(|(_, demand)| match demand.poll_state(waiter) {
467 track::DemandState::Active => true,
468 track::DemandState::Idle => false,
471 track::DemandState::Closed => false,
473 })
474 .map(|(want, _)| want)
475 .collect();
476
477 let grant = self
478 .estimate
479 .peek()
480 .and_then(|estimate| allocate(estimate, &wants, self.id));
481 Poll::Ready(Ok(grant))
482 }
483
484 fn claims(&self) -> Vec<(Want, track::Demand)> {
486 self.registry
487 .read()
488 .entries
489 .iter()
490 .map(|entry| {
491 (
492 Want {
493 id: entry.id,
494 priority: entry.priority,
495 max: entry.max,
496 },
497 entry.demand.clone(),
498 )
499 })
500 .collect()
501 }
502}
503
504fn allocate(estimate: Rate, wants: &[Want], id: u64) -> Option<Rate> {
517 let mut budget = estimate.as_bps();
520 let mut tier = wants.iter().map(|want| want.priority).max();
521
522 while let Some(priority) = tier {
523 let mut members: Vec<&Want> = wants.iter().filter(|want| want.priority == priority).collect();
526 members.sort_by_key(|want| want.max);
527
528 let mut remaining = members.len() as u64;
529 for want in members {
530 let even = budget / remaining;
531 let grant = want.max.as_bps().min(even);
532 if want.id == id {
533 return Some(Rate::from_bps(grant));
534 }
535 budget -= grant;
536 remaining -= 1;
537 }
538
539 tier = wants
540 .iter()
541 .map(|want| want.priority)
542 .filter(|other| *other < priority)
543 .max();
544 }
545
546 None
547}
548
549#[derive(Clone)]
551pub struct Consumer {
552 inner: Inner,
553 last: Option<Rate>,
554}
555
556#[derive(Clone)]
558enum Inner {
559 Whole(kio::Consumer<State>),
560 Share(Box<Share>),
561 Unavailable,
563}
564
565impl Consumer {
566 pub fn peek(&self) -> Option<Rate> {
568 match &self.inner {
569 Inner::Whole(state) => state.read().bitrate,
570 Inner::Share(share) => share.grant(),
571 Inner::Unavailable => None,
572 }
573 }
574
575 pub fn poll_changed(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<Rate>>> {
586 let last = self.last;
587
588 let bitrate = match &mut self.inner {
589 Inner::Whole(state) => match state.poll(waiter, |state| {
590 if state.bitrate != last {
591 Poll::Ready(state.bitrate)
592 } else {
593 Poll::Pending
594 }
595 }) {
596 Poll::Ready(Ok(bitrate)) => bitrate,
597 Poll::Ready(Err(state)) => return Poll::Ready(Err(state.abort.clone().unwrap_or(Error::Dropped))),
602 Poll::Pending => return Poll::Pending,
603 },
604 Inner::Unavailable => return Poll::Pending,
610 Inner::Share(share) => match share.poll_grant(waiter) {
611 Poll::Ready(Ok(grant)) if grant == last => return Poll::Pending,
612 Poll::Ready(Ok(grant)) => grant,
613 Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
614 Poll::Pending => return Poll::Pending,
615 },
616 };
617
618 self.last = bitrate;
619 Poll::Ready(Ok(bitrate))
620 }
621
622 pub async fn changed(&mut self) -> Result<Option<Rate>> {
630 kio::wait(|waiter| self.poll_changed(waiter)).await
631 }
632}
633
634#[cfg(test)]
635mod tests {
636 use super::*;
637 use crate::broadcast;
638
639 const AUDIO: u8 = 80;
641 const VIDEO: u8 = 60;
642
643 fn bps(bps: u64) -> Rate {
645 Rate::from_bps(bps)
646 }
647
648 fn want(id: u64, priority: u8, max: u64) -> Want {
649 Want {
650 id,
651 priority,
652 max: bps(max),
653 }
654 }
655
656 fn track(priority: u8) -> (broadcast::Producer, track::Producer) {
658 let broadcast = broadcast::Info::default().produce();
659 let track = broadcast
660 .create_track("t", track::Info::default().with_priority(priority))
661 .unwrap();
662 (broadcast, track)
663 }
664
665 #[test]
666 fn strict_priority_fills_the_top_tier_first() {
667 let wants = [want(0, AUDIO, 128_000), want(1, VIDEO, 4_000_000)];
668
669 assert_eq!(allocate(bps(2_000_000), &wants, 0), Some(bps(128_000)));
672 assert_eq!(allocate(bps(2_000_000), &wants, 1), Some(bps(1_872_000)));
673 }
674
675 #[test]
676 fn a_starved_tier_gets_nothing() {
677 let wants = [want(0, AUDIO, 2_000_000), want(1, VIDEO, 4_000_000)];
678
679 assert_eq!(allocate(bps(1_000_000), &wants, 0), Some(bps(1_000_000)));
680 assert_eq!(allocate(bps(1_000_000), &wants, 1), Some(bps(0)));
683 }
684
685 #[test]
691 fn one_tier_still_serves_audio_before_video() {
692 let flat = [want(0, 0, 128_000), want(1, 0, 4_000_000)];
693 let tiered = [want(0, AUDIO, 128_000), want(1, VIDEO, 4_000_000)];
694
695 for wants in [flat, tiered] {
696 assert_eq!(allocate(bps(2_000_000), &wants, 0), Some(bps(128_000)));
697 assert_eq!(allocate(bps(2_000_000), &wants, 1), Some(bps(1_872_000)));
698 }
699 }
700
701 #[test]
702 fn an_even_tier_splits_evenly() {
703 let wants = [want(0, VIDEO, 4_000_000), want(1, VIDEO, 4_000_000)];
704
705 assert_eq!(allocate(bps(6_000_000), &wants, 0), Some(bps(3_000_000)));
706 assert_eq!(allocate(bps(6_000_000), &wants, 1), Some(bps(3_000_000)));
707 }
708
709 #[test]
712 fn a_small_share_frees_what_it_does_not_want() {
713 let wants = [want(0, VIDEO, 1_000_000), want(1, VIDEO, 8_000_000)];
714
715 assert_eq!(allocate(bps(6_000_000), &wants, 0), Some(bps(1_000_000)));
716 assert_eq!(allocate(bps(6_000_000), &wants, 1), Some(bps(5_000_000)));
717 }
718
719 #[test]
723 fn surplus_is_left_unclaimed() {
724 assert_eq!(
725 allocate(bps(10_000_000), &[want(0, VIDEO, 4_000_000)], 0),
726 Some(bps(4_000_000))
727 );
728 }
729
730 #[test]
731 fn an_unregistered_share_has_no_grant() {
732 assert_eq!(allocate(bps(1_000_000), &[], 0), None);
733 assert_eq!(allocate(bps(1_000_000), &[want(0, VIDEO, 1_000)], 7), None);
734 }
735
736 #[tokio::test]
739 async fn concurrent_tracks_split_the_estimate() {
740 let estimate = Producer::new();
741 let allocator = Allocator::new(estimate.consume());
742
743 let (_first_broadcast, first) = track(VIDEO);
744 let _first_sub = first.consume();
745 let first = allocator.reserve(&first.demand(), bps(4_000_000));
746 let mut first_share = first.consumer();
747
748 estimate.set(Some(bps(2_000_000))).unwrap();
749 assert_eq!(first_share.changed().await.unwrap(), Some(bps(2_000_000)));
751
752 let (_second_broadcast, second) = track(VIDEO);
755 let _second_sub = second.consume();
756 let second_share = allocator.reserve(&second.demand(), bps(4_000_000));
757
758 assert_eq!(first_share.changed().await.unwrap(), Some(bps(1_000_000)));
759 assert_eq!(second_share.peek(), Some(bps(1_000_000)));
760 }
761
762 #[tokio::test]
765 async fn an_idle_track_claims_nothing() {
766 let estimate = Producer::new();
767 let allocator = Allocator::new(estimate.consume());
768 estimate.set(Some(bps(2_000_000))).unwrap();
769
770 let (_watched_broadcast, watched) = track(VIDEO);
771 let _watched_sub = watched.consume();
772 let watched_share = allocator.reserve(&watched.demand(), bps(4_000_000));
773
774 let (_idle_broadcast, idle) = track(VIDEO);
775 let idle_share = allocator.reserve(&idle.demand(), bps(4_000_000));
776
777 assert_eq!(watched_share.peek(), Some(bps(2_000_000)));
778 assert_eq!(idle_share.peek(), None);
781
782 let _idle_sub = idle.consume();
784 assert_eq!(watched_share.peek(), Some(bps(1_000_000)));
785 assert_eq!(idle_share.peek(), Some(bps(1_000_000)));
786 }
787
788 #[tokio::test]
791 async fn a_share_wakes_when_a_sibling_goes_idle() {
792 let estimate = Producer::new();
793 let allocator = Allocator::new(estimate.consume());
794 estimate.set(Some(bps(2_000_000))).unwrap();
795
796 let (_mine_broadcast, mine) = track(VIDEO);
797 let _mine_sub = mine.consume();
798 let mine_share_reserved = allocator.reserve(&mine.demand(), bps(4_000_000));
799 let mut mine_share = mine_share_reserved.consumer();
800
801 let (_sibling_broadcast, sibling) = track(VIDEO);
802 let sibling_sub = sibling.consume();
803 let _sibling_share = allocator.reserve(&sibling.demand(), bps(4_000_000));
804
805 assert_eq!(mine_share.changed().await.unwrap(), Some(bps(1_000_000)));
806
807 drop(sibling_sub);
809 assert_eq!(mine_share.changed().await.unwrap(), Some(bps(2_000_000)));
810 }
811
812 #[derive(Default)]
816 struct Woken(std::sync::atomic::AtomicBool);
817
818 impl std::task::Wake for Woken {
819 fn wake(self: std::sync::Arc<Self>) {
820 self.wake_by_ref();
821 }
822
823 fn wake_by_ref(self: &std::sync::Arc<Self>) {
824 self.0.store(true, std::sync::atomic::Ordering::SeqCst);
825 }
826 }
827
828 impl Woken {
829 fn new() -> (std::sync::Arc<Self>, kio::Waiter) {
833 let flag = std::sync::Arc::new(Self::default());
834 let waiter = kio::Waiter::new(std::task::Waker::from(flag.clone()));
835 (flag, waiter)
836 }
837
838 fn woken(&self) -> bool {
839 self.0.load(std::sync::atomic::Ordering::SeqCst)
840 }
841 }
842
843 #[tokio::test]
848 async fn an_unchanged_slice_keeps_watching_the_estimate() {
849 let estimate = Producer::new();
850 let allocator = Allocator::new(estimate.consume());
851
852 let (_broadcast, track) = track(VIDEO);
853 let _sub = track.consume();
854 let share_reserved = allocator.reserve(&track.demand(), bps(4_000_000));
855 let mut share = share_reserved.consumer();
856
857 estimate.set(Some(bps(10_000_000))).unwrap();
858 assert_eq!(share.changed().await.unwrap(), Some(bps(4_000_000)));
859
860 let (woken, waiter) = Woken::new();
863 estimate.set(Some(bps(9_000_000))).unwrap();
864 assert!(share.poll_changed(&waiter).is_pending());
865
866 estimate.set(Some(bps(1_000_000))).unwrap();
868 assert!(woken.woken(), "a parked share must be woken by the next estimate");
869 assert_eq!(share.changed().await.unwrap(), Some(bps(1_000_000)));
870 }
871
872 #[tokio::test]
874 async fn a_parked_share_is_woken_by_sibling_demand() {
875 let estimate = Producer::new();
876 let allocator = Allocator::new(estimate.consume());
877 estimate.set(Some(bps(2_000_000))).unwrap();
878
879 let (_mine_broadcast, mine) = track(VIDEO);
880 let _mine_sub = mine.consume();
881 let share_reserved = allocator.reserve(&mine.demand(), bps(4_000_000));
882 let mut share = share_reserved.consumer();
883
884 let (_sibling_broadcast, sibling) = track(VIDEO);
885 let sibling_sub = sibling.consume();
886 let _sibling_share = allocator.reserve(&sibling.demand(), bps(4_000_000));
887
888 assert_eq!(share.changed().await.unwrap(), Some(bps(1_000_000)));
889
890 let (woken, waiter) = Woken::new();
891 assert!(share.poll_changed(&waiter).is_pending());
892 drop(sibling_sub);
893 assert!(woken.woken(), "a sibling going idle must wake a parked share");
894 }
895
896 #[tokio::test]
899 async fn a_share_follows_the_estimate_lifecycle() {
900 let estimate = Producer::new();
901 let allocator = Allocator::new(estimate.consume());
902
903 let (_broadcast, track) = track(VIDEO);
904 let _sub = track.consume();
905 let share_reserved = allocator.reserve(&track.demand(), bps(4_000_000));
906 let mut share = share_reserved.consumer();
907
908 estimate.set(Some(bps(2_000_000))).unwrap();
909 assert_eq!(share.changed().await.unwrap(), Some(bps(2_000_000)));
910
911 estimate.set(None).unwrap();
912 assert_eq!(share.changed().await.unwrap(), None);
913
914 estimate.abort(Error::Cancel).unwrap();
915 assert!(share.changed().await.is_err());
916 assert!(share.changed().await.is_err());
917 }
918
919 #[tokio::test]
922 async fn a_closed_track_is_pruned() {
923 let estimate = Producer::new();
924 let allocator = Allocator::new(estimate.consume());
925
926 let (_first_broadcast, first) = track(VIDEO);
929 let _first_share = allocator.reserve(&first.demand(), bps(4_000_000));
930 first.abort(Error::Cancel).unwrap();
931
932 let (_second_broadcast, second) = track(VIDEO);
933 let _second_share = allocator.reserve(&second.demand(), bps(4_000_000));
934
935 assert_eq!(allocator.registry.read().entries.len(), 1);
936 }
937
938 #[tokio::test]
942 async fn dropping_a_reservation_releases_it() {
943 let estimate = Producer::new();
944 let allocator = Allocator::new(estimate.consume());
945 estimate.set(Some(bps(2_000_000))).unwrap();
946
947 let (_first_broadcast, first) = track(VIDEO);
948 let _first_sub = first.consume();
949 let first_reserved = allocator.reserve(&first.demand(), bps(4_000_000));
950
951 let (_second_broadcast, second) = track(VIDEO);
952 let _second_sub = second.consume();
953 let second_reserved = allocator.reserve(&second.demand(), bps(4_000_000));
954 assert_eq!(second_reserved.peek(), Some(bps(1_000_000)));
955
956 let mut orphan = first_reserved.consumer();
959 assert_eq!(orphan.changed().await.unwrap(), Some(bps(1_000_000)));
960
961 drop(first_reserved);
962 assert_eq!(allocator.registry.read().entries.len(), 1);
963 assert_eq!(second_reserved.peek(), Some(bps(2_000_000)));
964
965 assert_eq!(orphan.changed().await.unwrap(), None);
969 assert_eq!(orphan.peek(), None);
970 }
971
972 #[tokio::test]
976 async fn update_changes_the_claim_in_place() {
977 let estimate = Producer::new();
978 let allocator = Allocator::new(estimate.consume());
979 estimate.set(Some(bps(6_000_000))).unwrap();
980
981 let (_small_broadcast, small) = track(VIDEO);
982 let _small_sub = small.consume();
983 let small_reserved = allocator.reserve(&small.demand(), bps(1_000_000));
984
985 let (_large_broadcast, large) = track(VIDEO);
986 let _large_sub = large.consume();
987 let large_reserved = allocator.reserve(&large.demand(), bps(8_000_000));
988
989 assert_eq!(small_reserved.peek(), Some(bps(1_000_000)));
991 assert_eq!(large_reserved.peek(), Some(bps(5_000_000)));
992
993 small_reserved.update(bps(4_000_000));
996 assert_eq!(allocator.registry.read().entries.len(), 2);
997 assert_eq!(small_reserved.peek(), Some(bps(3_000_000)));
998 assert_eq!(large_reserved.peek(), Some(bps(3_000_000)));
999
1000 small_reserved.update(bps(1_000_000));
1002 assert_eq!(large_reserved.peek(), Some(bps(5_000_000)));
1003 }
1004
1005 #[tokio::test]
1008 async fn update_wakes_a_parked_reader() {
1009 let estimate = Producer::new();
1010 let allocator = Allocator::new(estimate.consume());
1011 estimate.set(Some(bps(6_000_000))).unwrap();
1012
1013 let (_broadcast, producer) = track(VIDEO);
1014 let _sub = producer.consume();
1015 let reserved = allocator.reserve(&producer.demand(), bps(1_000_000));
1016 let mut share = reserved.consumer();
1017 assert_eq!(share.changed().await.unwrap(), Some(bps(1_000_000)));
1018
1019 let (woken, waiter) = Woken::new();
1020 assert!(share.poll_changed(&waiter).is_pending());
1021
1022 reserved.update(bps(4_000_000));
1023 assert!(woken.woken(), "raising the ceiling must wake the reader");
1024 assert_eq!(share.changed().await.unwrap(), Some(bps(4_000_000)));
1025 }
1026
1027 #[tokio::test]
1030 async fn a_share_outliving_the_allocator_reports_closed() {
1031 let estimate = Producer::new();
1032 let allocator = Allocator::new(estimate.consume());
1033
1034 let (_broadcast, producer) = track(VIDEO);
1035 let _sub = producer.consume();
1036 let share_reserved = allocator.reserve(&producer.demand(), bps(4_000_000));
1037 let mut share = share_reserved.consumer();
1038
1039 drop(allocator);
1040 drop(estimate);
1041 assert!(share.changed().await.is_err());
1042 }
1043
1044 #[tokio::test]
1049 async fn closed_is_distinct_from_unavailable() {
1050 let producer = Producer::new();
1051 let mut consumer = producer.consume();
1052
1053 producer.set(Some(bps(1_000_000))).unwrap();
1054 assert_eq!(consumer.changed().await.unwrap(), Some(bps(1_000_000)));
1055
1056 producer.set(None).unwrap();
1058 assert_eq!(consumer.changed().await.unwrap(), None);
1059
1060 producer.abort(Error::Cancel).unwrap();
1062 assert!(consumer.changed().await.is_err());
1063 assert!(consumer.changed().await.is_err());
1065 }
1066}