Skip to content

Commit 88913ca

Browse files
committed
Implement a shared Stream
1 parent 0943bce commit 88913ca

6 files changed

Lines changed: 565 additions & 22 deletions

File tree

futures-util/src/future/future/shared.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::task::waker_ref;
2-
use crate::wakerset::{WakerSet, WakerKey};
2+
use crate::wakerset::{WakerKey, WakerSet};
33
use futures_core::future::{FusedFuture, Future};
44
use futures_core::task::{Context, Poll};
55
use std::cell::UnsafeCell;
@@ -243,11 +243,7 @@ where
243243

244244
inner.notifier.record_waker(&mut this.waker_key, cx);
245245

246-
match inner
247-
.state
248-
.compare_exchange(IDLE, POLLING, SeqCst, SeqCst)
249-
.unwrap_or_else(|x| x)
250-
{
246+
match inner.state.compare_exchange(IDLE, POLLING, SeqCst, SeqCst).unwrap_or_else(|x| x) {
251247
IDLE => {
252248
// Lock acquired, fall through
253249
}
@@ -296,8 +292,7 @@ where
296292

297293
match poll_result {
298294
Poll::Pending => {
299-
if inner.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok()
300-
{
295+
if inner.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok() {
301296
// Success
302297
drop(reset);
303298
this.inner = Some(inner);
@@ -352,6 +347,6 @@ impl<Fut: Future> WeakShared<Fut> {
352347
/// Returns [`None`] if all clones of the [`Shared`] have been dropped or polled
353348
/// to completion.
354349
pub fn upgrade(&self) -> Option<Shared<Fut>> {
355-
Some(Shared { inner: Some(self.0.upgrade()?), waker_key: NULL_WAKER_KEY })
350+
Some(Shared { inner: Some(self.0.upgrade()?), waker_key: WakerKey::NULL })
356351
}
357352
}

futures-util/src/stream/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub use self::stream::{
2525
};
2626

2727
#[cfg(feature = "std")]
28-
pub use self::stream::CatchUnwind;
28+
pub use self::stream::{CatchUnwind, Shared};
2929

3030
#[cfg(feature = "alloc")]
3131
pub use self::stream::Chunks;

futures-util/src/stream/stream/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ mod catch_unwind;
256256
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
257257
pub use self::catch_unwind::CatchUnwind;
258258

259+
#[cfg(feature = "std")]
260+
mod shared;
261+
#[cfg(feature = "std")]
262+
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
263+
pub use self::shared::Shared;
264+
259265
impl<T: ?Sized> StreamExt for T where T: Stream {}
260266

261267
/// An extension trait for `Stream`s that provides a variety of convenient
@@ -1501,6 +1507,72 @@ pub trait StreamExt: Stream {
15011507
assert_stream::<Self::Item, _>(Box::pin(self))
15021508
}
15031509

1510+
/// Create a cloneable handle to this stream where all handles will resolve
1511+
/// to the same result.
1512+
///
1513+
/// The shared() method provides a method to convert any stream into a
1514+
/// cloneable stream. It enables a stream to be polled by multiple threads.
1515+
///
1516+
/// This method is only available when the `std` feature of this library is
1517+
/// activiated, and it is activated by default.
1518+
///
1519+
/// # Panics
1520+
/// If the capacity is zero. It must have space for at least one item.
1521+
///
1522+
/// # Examples
1523+
///
1524+
/// ```
1525+
/// use futures::executor::block_on;
1526+
/// use futures::stream::{self, StreamExt};
1527+
///
1528+
/// let stream = stream::iter(1..=3);
1529+
/// let shared1 = stream.shared(4);
1530+
/// let shared2 = shared1.clone();
1531+
///
1532+
/// assert_eq!(vec![1,2,3], block_on(shared1.collect::<Vec<_>>()));
1533+
/// assert_eq!(vec![1,2,3], block_on(shared2.collect::<Vec<_>>()));
1534+
/// ```
1535+
///
1536+
/// ```
1537+
/// use futures::executor::block_on;
1538+
/// use futures::stream::{self, StreamExt};
1539+
/// use std::thread;
1540+
///
1541+
/// let stream = stream::iter(1..=3);
1542+
/// let shared1 = stream.shared(4);
1543+
/// let shared2 = shared1.clone();
1544+
/// let join_handle = thread::spawn(move || {
1545+
/// assert_eq!(vec![1,2,3], block_on(shared2.collect::<Vec<_>>()));
1546+
/// });
1547+
/// assert_eq!(vec![1,2,3], block_on(shared1.collect::<Vec<_>>()));
1548+
/// join_handle.join().unwrap();
1549+
/// ```
1550+
///
1551+
/// ```
1552+
/// # futures::executor::block_on(async {
1553+
/// use futures::stream::{self, StreamExt};
1554+
///
1555+
/// let stream = stream::iter(vec![1,2,3]);
1556+
/// let mut shared1 = stream.shared(4);
1557+
///
1558+
/// assert_eq!(Some(1), shared1.next().await);
1559+
///
1560+
/// let mut shared2 = shared1.clone();
1561+
/// assert_eq!(Some(2), shared2.next().await);
1562+
/// assert_eq!(Some(3), shared2.next().await);
1563+
/// assert_eq!(vec![2,3], shared1.collect::<Vec<_>>().await);
1564+
/// assert_eq!(None, shared2.next().await);
1565+
/// # });
1566+
/// ```
1567+
#[cfg(feature = "std")]
1568+
fn shared(self, capacity: usize) -> Shared<Self>
1569+
where
1570+
Self: Sized,
1571+
Self::Item: Clone,
1572+
{
1573+
Shared::new(self, capacity)
1574+
}
1575+
15041576
/// An adaptor for creating a buffered list of pending futures.
15051577
///
15061578
/// If this stream's item can be converted into a future, then this adaptor

0 commit comments

Comments
 (0)