1use std::{
5 collections::VecDeque,
6 sync::{Arc, Mutex},
7 task::Waker,
8};
9
10use futures::{Stream, StreamExt, stream::BoxStream};
11use pin_project::{pin_project, pinned_drop};
12use tokio::sync::Semaphore;
13use tokio_util::sync::PollSemaphore;
14
15#[derive(Clone, Copy, Debug, PartialEq)]
16enum Side {
17 Left,
18 Right,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq)]
23pub enum Capacity {
24 Bounded(u32),
25 Unbounded,
26}
27
28struct InnerState<'a, T> {
29 inner: Option<BoxStream<'a, T>>,
30 buffer: VecDeque<T>,
31 polling: Option<Side>,
32 waker: Option<Waker>,
33 exhausted: bool,
34 left_buffered: u32,
35 right_buffered: u32,
36 left_dropped: bool,
37 right_dropped: bool,
38 available_buffer: Option<PollSemaphore>,
39}
40
41pub struct SharedStream<'a, T: Clone> {
43 state: Arc<Mutex<InnerState<'a, T>>>,
44 side: Side,
45}
46
47impl<'a, T: Clone> SharedStream<'a, T> {
48 pub fn new(inner: BoxStream<'a, T>, capacity: Capacity) -> (Self, Self) {
49 let available_buffer = match capacity {
50 Capacity::Unbounded => None,
51 Capacity::Bounded(capacity) => Some(PollSemaphore::new(Arc::new(Semaphore::new(
52 capacity as usize,
53 )))),
54 };
55 let state = InnerState {
56 inner: Some(inner),
57 buffer: VecDeque::new(),
58 polling: None,
59 waker: None,
60 exhausted: false,
61 left_buffered: 0,
62 right_buffered: 0,
63 left_dropped: false,
64 right_dropped: false,
65 available_buffer,
66 };
67
68 let state = Arc::new(Mutex::new(state));
69
70 let left = Self {
71 state: state.clone(),
72 side: Side::Left,
73 };
74 let right = Self {
75 state,
76 side: Side::Right,
77 };
78 (left, right)
79 }
80}
81
82impl<T: Clone> Drop for SharedStream<'_, T> {
83 fn drop(&mut self) {
84 let Ok(mut inner_state) = self.state.lock() else {
85 return;
86 };
87 match self.side {
88 Side::Left => inner_state.left_dropped = true,
89 Side::Right => inner_state.right_dropped = true,
90 }
91 let pending_for_self = match self.side {
97 Side::Left => std::mem::take(&mut inner_state.left_buffered),
98 Side::Right => std::mem::take(&mut inner_state.right_buffered),
99 };
100 debug_assert!(
101 pending_for_self == 0 || inner_state.buffer.len() as u32 == pending_for_self,
102 "buffer holds items for both sides"
103 );
104 for _ in 0..pending_for_self {
105 inner_state.buffer.pop_front();
106 if let Some(available_buffer) = inner_state.available_buffer.as_mut() {
107 available_buffer.add_permits(1);
108 }
109 }
110 let was_polling = inner_state.polling == Some(self.side);
114 let to_wake = if was_polling {
115 inner_state.polling = None;
116 inner_state.waker.take()
117 } else {
118 None
119 };
120 drop(inner_state);
121 if let Some(waker) = to_wake {
122 waker.wake();
123 }
124 }
125}
126
127impl<T: Clone> Stream for SharedStream<'_, T> {
128 type Item = T;
129
130 fn poll_next(
131 self: std::pin::Pin<&mut Self>,
132 cx: &mut std::task::Context<'_>,
133 ) -> std::task::Poll<Option<Self::Item>> {
134 let mut inner_state = self.state.lock().unwrap();
135 let can_take_buffered = match self.side {
136 Side::Left => inner_state.left_buffered > 0,
137 Side::Right => inner_state.right_buffered > 0,
138 };
139 if can_take_buffered {
140 let item = inner_state.buffer.pop_front();
142 match self.side {
143 Side::Left => {
144 inner_state.left_buffered -= 1;
145 }
146 Side::Right => {
147 inner_state.right_buffered -= 1;
148 }
149 }
150 if let Some(available_buffer) = inner_state.available_buffer.as_mut() {
151 available_buffer.add_permits(1);
152 }
153 std::task::Poll::Ready(item)
154 } else {
155 if inner_state.exhausted {
156 return std::task::Poll::Ready(None);
157 }
158 let permit = if let Some(available_buffer) = inner_state.available_buffer.as_mut() {
160 match available_buffer.poll_acquire(cx) {
161 std::task::Poll::Ready(permit) => Some(permit.unwrap()),
164 std::task::Poll::Pending => {
165 return std::task::Poll::Pending;
166 }
167 }
168 } else {
169 None
170 };
171 if let Some(polling_side) = inner_state.polling.as_ref()
172 && *polling_side != self.side
173 {
174 inner_state.waker = Some(cx.waker().clone());
182 return std::task::Poll::Pending;
183 }
184 inner_state.polling = Some(self.side);
185 let mut to_poll = inner_state
187 .inner
188 .take()
189 .expect("Other half of shared stream panic'd while polling inner stream");
190 drop(inner_state);
191 let res = to_poll.poll_next_unpin(cx);
192 let mut inner_state = self.state.lock().unwrap();
193
194 let mut should_wake = true;
195 match &res {
196 std::task::Poll::Ready(None) => {
197 inner_state.exhausted = true;
198 inner_state.polling = None;
199 }
200 std::task::Poll::Ready(Some(item)) => {
201 inner_state.polling = None;
202 let other_alive = match self.side {
206 Side::Left => !inner_state.right_dropped,
207 Side::Right => !inner_state.left_dropped,
208 };
209 if other_alive {
210 if let Some(permit) = permit {
212 permit.forget();
213 }
214 match self.side {
215 Side::Left => {
216 inner_state.right_buffered += 1;
217 }
218 Side::Right => {
219 inner_state.left_buffered += 1;
220 }
221 };
222 inner_state.buffer.push_back(item.clone());
223 }
224 }
225 std::task::Poll::Pending => {
226 should_wake = false;
227 }
228 };
229
230 inner_state.inner = Some(to_poll);
231
232 let to_wake = if should_wake {
234 inner_state.waker.take()
235 } else {
236 None
239 };
240 drop(inner_state);
241 if let Some(waker) = to_wake {
242 waker.wake();
243 }
244 res
245 }
246 }
247}
248
249pub trait SharedStreamExt<'a>: Stream + Send
250where
251 Self::Item: Clone,
252{
253 fn share(
266 self,
267 capacity: Capacity,
268 ) -> (SharedStream<'a, Self::Item>, SharedStream<'a, Self::Item>);
269}
270
271impl<'a, T: Clone> SharedStreamExt<'a> for BoxStream<'a, T> {
272 fn share(self, capacity: Capacity) -> (SharedStream<'a, T>, SharedStream<'a, T>) {
273 SharedStream::new(self, capacity)
274 }
275}
276
277#[pin_project]
278pub struct FinallyStream<S: Stream, F: FnOnce()> {
279 #[pin]
280 stream: S,
281 f: Option<F>,
282}
283
284impl<S: Stream, F: FnOnce()> FinallyStream<S, F> {
285 pub fn new(stream: S, f: F) -> Self {
286 Self { stream, f: Some(f) }
287 }
288}
289
290impl<S: Stream, F: FnOnce()> Stream for FinallyStream<S, F> {
291 type Item = S::Item;
292
293 fn poll_next(
294 self: std::pin::Pin<&mut Self>,
295 cx: &mut std::task::Context<'_>,
296 ) -> std::task::Poll<Option<Self::Item>> {
297 let this = self.project();
298 let res = this.stream.poll_next(cx);
299 if matches!(res, std::task::Poll::Ready(None)) {
300 if let Some(f) = this.f.take() {
302 f();
303 }
304 }
305 res
306 }
307}
308
309pub trait FinallyStreamExt<S: Stream>: Stream + Sized {
310 fn finally<F: FnOnce()>(self, f: F) -> FinallyStream<Self, F> {
311 FinallyStream {
312 stream: self,
313 f: Some(f),
314 }
315 }
316}
317
318impl<S: Stream> FinallyStreamExt<S> for S {
319 fn finally<F: FnOnce()>(self, f: F) -> FinallyStream<Self, F> {
320 FinallyStream::new(self, f)
321 }
322}
323
324#[pin_project(PinnedDrop)]
330pub struct OnDropStream<S: Stream, F: FnOnce()> {
331 #[pin]
332 stream: S,
333 f: Option<F>,
334}
335
336impl<S: Stream, F: FnOnce()> OnDropStream<S, F> {
337 pub fn new(stream: S, f: F) -> Self {
338 Self { stream, f: Some(f) }
339 }
340}
341
342impl<S: Stream, F: FnOnce()> Stream for OnDropStream<S, F> {
343 type Item = S::Item;
344
345 fn poll_next(
346 self: std::pin::Pin<&mut Self>,
347 cx: &mut std::task::Context<'_>,
348 ) -> std::task::Poll<Option<Self::Item>> {
349 self.project().stream.poll_next(cx)
350 }
351}
352
353#[pinned_drop]
354impl<S: Stream, F: FnOnce()> PinnedDrop for OnDropStream<S, F> {
355 fn drop(self: std::pin::Pin<&mut Self>) {
356 let this = self.project();
357 if let Some(f) = this.f.take() {
358 f();
359 }
360 }
361}
362
363pub trait StreamOnDropExt: Stream + Sized {
364 fn on_drop<F: FnOnce()>(self, f: F) -> OnDropStream<Self, F> {
366 OnDropStream::new(self, f)
367 }
368}
369
370impl<S: Stream> StreamOnDropExt for S {}
371
372#[cfg(test)]
373mod tests {
374
375 use std::sync::Arc;
376 use std::sync::atomic::{AtomicBool, Ordering};
377
378 use futures::{FutureExt, StreamExt};
379 use tokio_stream::wrappers::ReceiverStream;
380
381 use crate::utils::futures::{Capacity, SharedStreamExt, StreamOnDropExt};
382
383 fn is_pending(fut: &mut (impl std::future::Future + Unpin)) -> bool {
384 let noop_waker = futures::task::noop_waker();
385 let mut context = std::task::Context::from_waker(&noop_waker);
386 fut.poll_unpin(&mut context).is_pending()
387 }
388
389 #[tokio::test]
390 async fn test_shared_stream() {
391 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
392 let inner_stream = ReceiverStream::new(rx);
393
394 for i in 0..3 {
396 tx.send(i).await.unwrap();
397 }
398
399 let (mut left, mut right) = inner_stream.boxed().share(Capacity::Bounded(2));
400
401 assert_eq!(left.next().await.unwrap(), 0);
403 assert_eq!(left.next().await.unwrap(), 1);
404
405 let mut left_fut = left.next();
407
408 assert!(is_pending(&mut left_fut));
409
410 assert_eq!(right.next().await.unwrap(), 0);
412 assert_eq!(left_fut.await.unwrap(), 2);
413
414 assert_eq!(right.next().await.unwrap(), 1);
416 assert_eq!(right.next().await.unwrap(), 2);
417
418 let mut right_fut = right.next();
420 let mut left_fut = left.next();
421 assert!(is_pending(&mut right_fut));
422 assert!(is_pending(&mut left_fut));
423
424 tx.send(3).await.unwrap();
426
427 assert_eq!(right_fut.await.unwrap(), 3);
429 assert_eq!(left_fut.await.unwrap(), 3);
430
431 drop(tx);
432
433 assert_eq!(left.next().await, None);
435 assert_eq!(right.next().await, None);
436
437 assert_eq!(left.next().await, None);
439 assert_eq!(right.next().await, None);
440 }
441
442 #[test]
443 fn test_shared_stream_replaces_waiting_waker() {
444 let inner_stream = futures::stream::pending::<u32>();
445 let (mut left, mut right) = inner_stream.boxed().share(Capacity::Unbounded);
446
447 let mut left_fut = left.next();
448 assert!(is_pending(&mut left_fut));
449
450 let mut right_fut = right.next();
451 assert!(is_pending(&mut right_fut));
452 assert!(is_pending(&mut right_fut));
453 }
454
455 #[tokio::test]
456 async fn test_unbounded_shared_stream() {
457 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
458 let inner_stream = ReceiverStream::new(rx);
459
460 for i in 0..10 {
462 tx.send(i).await.unwrap();
463 }
464 drop(tx);
465
466 let (mut left, mut right) = inner_stream.boxed().share(Capacity::Unbounded);
467
468 for i in 0..10 {
470 assert_eq!(left.next().await.unwrap(), i);
471 }
472 assert_eq!(left.next().await, None);
473
474 for i in 0..10 {
476 assert_eq!(right.next().await.unwrap(), i);
477 }
478 assert_eq!(right.next().await, None);
479 }
480
481 #[tokio::test(flavor = "multi_thread")]
482 async fn stress_shared_stream() {
483 for _ in 0..100 {
484 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
485 let inner_stream = ReceiverStream::new(rx);
486 let (mut left, mut right) = inner_stream.boxed().share(Capacity::Bounded(2));
487
488 let left_handle = tokio::spawn(async move {
489 let mut counter = 0;
490 while let Some(item) = left.next().await {
491 assert_eq!(item, counter);
492 counter += 1;
493 }
494 });
495
496 let right_handle = tokio::spawn(async move {
497 let mut counter = 0;
498 while let Some(item) = right.next().await {
499 assert_eq!(item, counter);
500 counter += 1;
501 }
502 });
503
504 for i in 0..1000 {
505 tx.send(i).await.unwrap();
506 }
507 drop(tx);
508 left_handle.await.unwrap();
509 right_handle.await.unwrap();
510 }
511 }
512
513 #[tokio::test]
514 async fn test_on_drop_fires_on_early_drop() {
515 let called = Arc::new(AtomicBool::new(false));
516 let called_clone = called.clone();
517
518 let stream = futures::stream::iter(vec![1, 2, 3]);
519 let mut stream = stream.on_drop(move || {
520 called_clone.store(true, Ordering::SeqCst);
521 });
522
523 assert_eq!(stream.next().await, Some(1));
525 assert!(!called.load(Ordering::SeqCst));
526 drop(stream);
527 assert!(called.load(Ordering::SeqCst));
528 }
529
530 #[tokio::test]
531 async fn test_on_drop_fires_after_exhaustion() {
532 let called = Arc::new(AtomicBool::new(false));
533 let called_clone = called.clone();
534
535 let stream = futures::stream::iter(vec![1]);
536 let mut stream = stream.on_drop(move || {
537 called_clone.store(true, Ordering::SeqCst);
538 });
539
540 assert_eq!(stream.next().await, Some(1));
541 assert_eq!(stream.next().await, None);
542 assert!(!called.load(Ordering::SeqCst));
543 drop(stream);
544 assert!(called.load(Ordering::SeqCst));
545 }
546
547 #[tokio::test]
548 async fn test_on_drop_fires_without_polling() {
549 let called = Arc::new(AtomicBool::new(false));
550 let called_clone = called.clone();
551
552 let stream = futures::stream::iter(vec![1, 2, 3]);
553 let stream = stream.on_drop(move || {
554 called_clone.store(true, Ordering::SeqCst);
555 });
556
557 assert!(!called.load(Ordering::SeqCst));
558 drop(stream);
559 assert!(called.load(Ordering::SeqCst));
560 }
561
562 #[tokio::test]
570 async fn test_shared_stream_survivor_progresses_after_drop() {
571 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
572 for i in 0..6 {
573 tx.send(i).await.unwrap();
574 }
575 let (left, mut right) = ReceiverStream::new(rx).boxed().share(Capacity::Bounded(2));
576
577 assert_eq!(right.next().await.unwrap(), 0);
580 assert_eq!(right.next().await.unwrap(), 1);
581 let mut right_fut = right.next();
582 assert!(is_pending(&mut right_fut));
583
584 drop(left);
585
586 let item = tokio::time::timeout(std::time::Duration::from_secs(5), right_fut).await;
590 assert_eq!(item.expect("survivor wedged by dropped side").unwrap(), 2);
591 for expected in 3..6 {
592 let item = tokio::time::timeout(std::time::Duration::from_secs(5), right.next()).await;
593 assert_eq!(
594 item.expect("survivor wedged after capacity re-spent")
595 .unwrap(),
596 expected
597 );
598 }
599 }
600
601 #[tokio::test]
602 async fn test_shared_stream_dropped_poller_releases_inner_stream() {
603 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
604 let (mut left, mut right) = ReceiverStream::new(rx).boxed().share(Capacity::Unbounded);
605
606 let mut left_fut = left.next();
609 assert!(is_pending(&mut left_fut));
610 drop(left_fut);
611 drop(left);
612
613 tx.send(42).await.unwrap();
616 let item = tokio::time::timeout(std::time::Duration::from_secs(5), right.next()).await;
617 assert_eq!(
618 item.expect("survivor wedged by dropped poller").unwrap(),
619 42
620 );
621 }
622 struct FlagWaker(AtomicBool);
623
624 impl futures::task::ArcWake for FlagWaker {
625 fn wake_by_ref(arc_self: &Arc<Self>) {
626 arc_self.0.store(true, Ordering::SeqCst);
627 }
628 }
629
630 #[tokio::test]
634 async fn test_shared_stream_dropped_poller_wakes_parked_survivor() {
635 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
636 let (mut left, mut right) = ReceiverStream::new(rx).boxed().share(Capacity::Unbounded);
637
638 let mut left_fut = left.next();
640 assert!(is_pending(&mut left_fut));
641 drop(left_fut);
642
643 let flag = Arc::new(FlagWaker(AtomicBool::new(false)));
645 let flag_waker = futures::task::waker(flag.clone());
646 let mut cx = std::task::Context::from_waker(&flag_waker);
647 let mut right_fut = right.next();
648 assert!(right_fut.poll_unpin(&mut cx).is_pending());
649 assert!(!flag.0.load(Ordering::SeqCst));
650
651 drop(left);
652 assert!(
653 flag.0.load(Ordering::SeqCst),
654 "dropping the poller must wake the parked survivor"
655 );
656
657 tx.send(42).await.unwrap();
658 let item = tokio::time::timeout(std::time::Duration::from_secs(5), right_fut).await;
659 assert_eq!(
660 item.expect("survivor wedged by dropped poller").unwrap(),
661 42
662 );
663 }
664
665 #[tokio::test]
669 async fn test_shared_stream_unbounded_stops_buffering_for_dropped_side() {
670 let (tx, rx) = tokio::sync::mpsc::channel::<u32>(10);
671 for i in 0..4 {
672 tx.send(i).await.unwrap();
673 }
674 let (left, mut right) = ReceiverStream::new(rx).boxed().share(Capacity::Unbounded);
675
676 assert_eq!(right.next().await.unwrap(), 0);
678 assert_eq!(right.next().await.unwrap(), 1);
679 assert_eq!(right.state.lock().unwrap().buffer.len(), 2);
680
681 drop(left);
682 assert!(
683 right.state.lock().unwrap().buffer.is_empty(),
684 "the dropped side's buffered items must be released"
685 );
686
687 for expected in 2..4 {
688 let item = tokio::time::timeout(std::time::Duration::from_secs(5), right.next()).await;
689 assert_eq!(
690 item.expect("survivor wedged after peer drop").unwrap(),
691 expected
692 );
693 }
694 let state = right.state.lock().unwrap();
695 assert!(
696 state.buffer.is_empty(),
697 "no clone may be queued for a dropped side"
698 );
699 assert_eq!(state.left_buffered, 0);
700 }
701}