#![deny(missing_docs)]
#![warn(rust_2018_idioms, rustdoc::broken_intra_doc_links)]
extern crate alloc;
use alloc::sync::{Arc, Weak};
use core::cell::UnsafeCell;
use core::fmt::{self, Debug};
use core::iter::FromIterator;
use core::marker::PhantomData;
use core::mem;
use core::ops::{Index, IndexMut};
use core::pin::Pin;
use core::ptr;
use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
use core::sync::atomic::{AtomicBool, AtomicPtr};
use futures_core::stream::{FusedStream, Stream};
use futures_core::task::{Context, Poll};
use futures_util::task::{ArcWake, AtomicWaker};
mod abort;
mod iter;
pub use self::iter::{IterMut, IterMutWithToken, IterPinMut, IterPinMutWithToken, IterWithToken};
mod task;
use self::task::Task;
mod ready_to_run_queue;
use self::ready_to_run_queue::{Dequeue, ReadyToRunQueue};
const YIELD_EVERY: usize = 32;
#[must_use = "streams do nothing unless polled"]
pub struct StreamUnordered<S> {
ready_to_run_queue: Arc<ReadyToRunQueue<S>>,
head_all: AtomicPtr<Task<S>>,
is_terminated: AtomicBool,
by_id: slab::Slab<*const Task<S>>,
}
unsafe impl<S: Send> Send for StreamUnordered<S> {}
unsafe impl<S: Sync> Sync for StreamUnordered<S> {}
impl<S> Unpin for StreamUnordered<S> {}
#[derive(Debug)]
pub struct StreamEntry<'a, S> {
token: usize,
inserted: bool,
backref: &'a mut StreamUnordered<S>,
}
impl<'a, S: 'a> StreamEntry<'a, S> {
pub fn insert(mut self, stream: S) {
self.inserted = true;
unsafe {
(*(*self.backref.by_id[self.token]).stream.get()) = Some(stream);
}
}
pub fn token(&self) -> usize {
self.token
}
}
impl<'a, S: 'a> Drop for StreamEntry<'a, S> {
fn drop(&mut self) {
if !self.inserted {
let task_ptr = self.backref.by_id.remove(self.token);
let task = unsafe { self.backref.unlink(task_ptr) };
self.backref.release_task(task);
}
}
}
impl<S: Stream> StreamUnordered<S> {
pub fn new() -> StreamUnordered<S> {
let mut slab = slab::Slab::new();
let slot = slab.vacant_entry();
let stub = Arc::new(Task {
stream: UnsafeCell::new(None),
is_done: UnsafeCell::new(false),
next_all: AtomicPtr::new(ptr::null_mut()),
prev_all: UnsafeCell::new(ptr::null()),
len_all: UnsafeCell::new(0),
next_ready_to_run: AtomicPtr::new(ptr::null_mut()),
queued: AtomicBool::new(true),
ready_to_run_queue: Weak::new(),
id: slot.key(),
});
let stub_ptr = &*stub as *const Task<S>;
let _ = slab.insert(stub_ptr);
let ready_to_run_queue = Arc::new(ReadyToRunQueue {
waker: AtomicWaker::new(),
head: AtomicPtr::new(stub_ptr as *mut _),
tail: UnsafeCell::new(stub_ptr),
stub,
});
StreamUnordered {
head_all: AtomicPtr::new(ptr::null_mut()),
ready_to_run_queue,
is_terminated: AtomicBool::new(false),
by_id: slab,
}
}
}
impl<S: Stream> Default for StreamUnordered<S> {
fn default() -> StreamUnordered<S> {
StreamUnordered::new()
}
}
impl<S> StreamUnordered<S> {
pub fn len(&self) -> usize {
let (_, len) = self.atomic_load_head_and_len_all();
len
}
pub fn is_empty(&self) -> bool {
self.head_all.load(Relaxed).is_null()
}
pub fn stream_entry(&mut self) -> StreamEntry<'_, S> {
let next_all = self.pending_next_all();
let slot = self.by_id.vacant_entry();
let token = slot.key();
let task = Arc::new(Task {
stream: UnsafeCell::new(None),
is_done: UnsafeCell::new(false),
next_all: AtomicPtr::new(next_all),
prev_all: UnsafeCell::new(ptr::null_mut()),
len_all: UnsafeCell::new(0),
next_ready_to_run: AtomicPtr::new(ptr::null_mut()),
queued: AtomicBool::new(true),
ready_to_run_queue: Arc::downgrade(&self.ready_to_run_queue),
id: token,
});
let _ = slot.insert(&*task as *const _);
self.is_terminated.store(false, Relaxed);
let ptr = self.link(task);
self.ready_to_run_queue.enqueue(ptr);
StreamEntry {
token,
inserted: false,
backref: self,
}
}
#[deprecated(since = "0.5.2", note = "Prefer StreamUnordered::insert")]
pub fn push(&mut self, stream: S) -> usize {
self.insert(stream)
}
pub fn insert(&mut self, stream: S) -> usize {
let s = self.stream_entry();
let token = s.token();
s.insert(stream);
token
}
pub fn remove(mut self: Pin<&mut Self>, token: usize) -> bool {
if token == 0 {
return false;
}
let task = if let Some(task) = self.by_id.get(token) {
*task
} else {
return false;
};
let task = unsafe { self.unlink(task) };
self.release_task(task);
true
}
pub fn take(mut self: Pin<&mut Self>, token: usize) -> Option<S>
where
S: Unpin,
{
if token == 0 {
return None;
}
let task = *self.by_id.get(token)?;
let task = unsafe { self.unlink(task) };
let stream = unsafe { &mut *task.stream.get() }.take();
self.release_task(task);
stream
}
pub fn is_finished(&self, token: usize) -> Option<bool> {
if token == 0 {
return None;
}
Some(unsafe { *(**self.by_id.get(token)?).is_done.get() })
}
pub fn get(&self, token: usize) -> Option<&S> {
if token == 0 {
return None;
}
Some(unsafe { (*(**self.by_id.get(token)?).stream.get()).as_ref().unwrap() })
}
pub fn get_mut(&mut self, token: usize) -> Option<&mut S>
where
S: Unpin,
{
if token == 0 {
return None;
}
Some(unsafe {
(*(**self.by_id.get_mut(token)?).stream.get())
.as_mut()
.unwrap()
})
}
pub fn get_pin_mut(mut self: Pin<&mut Self>, token: usize) -> Option<Pin<&mut S>> {
if token == 0 {
return None;
}
Some(unsafe {
Pin::new_unchecked(
(*(**self.by_id.get_mut(token)?).stream.get())
.as_mut()
.unwrap(),
)
})
}
pub fn iter_mut(&mut self) -> IterMut<'_, S>
where
S: Unpin,
{
IterMut(Pin::new(self).iter_pin_mut_with_token())
}
pub fn iter_mut_with_token(&mut self) -> IterMutWithToken<'_, S>
where
S: Unpin,
{
IterMutWithToken(Pin::new(self).iter_pin_mut_with_token())
}
pub fn iter_pin_mut(self: Pin<&mut Self>) -> IterPinMut<'_, S> {
IterPinMut(self.iter_pin_mut_with_token())
}
pub fn iter_pin_mut_with_token(mut self: Pin<&mut Self>) -> IterPinMutWithToken<'_, S> {
let task = *self.head_all.get_mut();
let len = if task.is_null() {
0
} else {
unsafe { *(*task).len_all.get() }
};
IterPinMutWithToken {
task,
len,
_marker: PhantomData,
}
}
pub fn iter_with_token(&self) -> IterWithToken<'_, S> {
let (task, len) = self.atomic_load_head_and_len_all();
IterWithToken {
task,
len,
pending_next_all: self.pending_next_all(),
_marker: PhantomData,
}
}
fn atomic_load_head_and_len_all(&self) -> (*const Task<S>, usize) {
let task = self.head_all.load(Acquire);
let len = if task.is_null() {
0
} else {
unsafe {
(*task).spin_next_all(self.pending_next_all(), Acquire);
*(*task).len_all.get()
}
};
(task, len)
}
fn release_task(&mut self, task: Arc<Task<S>>) {
self.by_id.remove(task.id);
debug_assert_eq!(task.next_all.load(Relaxed), self.pending_next_all());
unsafe {
debug_assert!((*task.prev_all.get()).is_null());
}
let prev = task.queued.swap(true, SeqCst);
unsafe {
*task.stream.get() = None;
}
if prev {
mem::forget(task);
}
}
fn link(&self, task: Arc<Task<S>>) -> *const Task<S> {
debug_assert_eq!(task.next_all.load(Relaxed), self.pending_next_all());
let ptr = Arc::into_raw(task);
let next = self.head_all.swap(ptr as *mut _, AcqRel);
unsafe {
let new_len = if next.is_null() {
1
} else {
(*next).spin_next_all(self.pending_next_all(), Acquire);
*(*next).len_all.get() + 1
};
*(*ptr).len_all.get() = new_len;
(*ptr).next_all.store(next, Release);
if !next.is_null() {
*(*next).prev_all.get() = ptr;
}
}
ptr
}
unsafe fn unlink(&mut self, task: *const Task<S>) -> Arc<Task<S>> {
let head = *self.head_all.get_mut();
debug_assert!(!head.is_null());
let new_len = *(*head).len_all.get() - 1;
let task = Arc::from_raw(task);
let next = task.next_all.load(Relaxed);
let prev = *task.prev_all.get();
task.next_all.store(self.pending_next_all(), Relaxed);
*task.prev_all.get() = ptr::null_mut();
if !next.is_null() {
*(*next).prev_all.get() = prev;
}
if !prev.is_null() {
(*prev).next_all.store(next, Relaxed);
} else {
*self.head_all.get_mut() = next;
}
let head = *self.head_all.get_mut();
if !head.is_null() {
*(*head).len_all.get() = new_len;
}
task
}
fn pending_next_all(&self) -> *mut Task<S> {
&*self.ready_to_run_queue.stub as *const _ as *mut _
}
}
impl<S> Index<usize> for StreamUnordered<S> {
type Output = S;
fn index(&self, stream: usize) -> &Self::Output {
self.get(stream).unwrap()
}
}
impl<S> IndexMut<usize> for StreamUnordered<S>
where
S: Unpin,
{
fn index_mut(&mut self, stream: usize) -> &mut Self::Output {
self.get_mut(stream).unwrap()
}
}
pub enum StreamYield<S>
where
S: Stream,
{
Item(S::Item),
Finished(FinishedStream),
}
#[must_use]
pub struct FinishedStream {
token: usize,
}
impl FinishedStream {
pub fn remove<S>(self, so: Pin<&mut StreamUnordered<S>>) {
so.remove(self.token);
}
pub fn take<S>(self, so: Pin<&mut StreamUnordered<S>>) -> Option<S>
where
S: Unpin,
{
so.take(self.token)
}
pub fn keep(self) {}
pub fn token(self) -> usize {
self.token
}
}
impl<S> Debug for StreamYield<S>
where
S: Stream,
S::Item: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StreamYield::Item(ref i) => f.debug_tuple("StreamYield::Item").field(i).finish(),
StreamYield::Finished(_) => f.debug_tuple("StreamYield::Finished").finish(),
}
}
}
impl<S> PartialEq for StreamYield<S>
where
S: Stream,
S::Item: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(StreamYield::Item(s), StreamYield::Item(o)) => s == o,
_ => false,
}
}
}
impl<S: Stream + 'static> Stream for StreamUnordered<S> {
type Item = (StreamYield<S>, usize);
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut polled = 0;
self.ready_to_run_queue.waker.register(cx.waker());
loop {
let task = match unsafe { self.ready_to_run_queue.dequeue() } {
Dequeue::Empty => {
if self.is_empty() {
*self.is_terminated.get_mut() = true;
return Poll::Ready(None);
} else {
return Poll::Pending;
}
}
Dequeue::Inconsistent => {
cx.waker().wake_by_ref();
return Poll::Pending;
}
Dequeue::Data(task) => task,
};
debug_assert!(task != self.ready_to_run_queue.stub());
let stream = match unsafe { &mut *(*task).stream.get() } {
Some(stream) => stream,
None => {
let task = unsafe { Arc::from_raw(task) };
debug_assert_eq!(task.next_all.load(Relaxed), self.pending_next_all());
unsafe {
debug_assert!((*task.prev_all.get()).is_null());
}
continue;
}
};
if unsafe { *(*task).is_done.get() } {
continue;
}
let task = unsafe { self.unlink(task) };
let prev = task.queued.swap(false, SeqCst);
assert!(prev);
struct Bomb<'a, S> {
queue: &'a mut StreamUnordered<S>,
task: Option<Arc<Task<S>>>,
}
impl<S> Drop for Bomb<'_, S> {
fn drop(&mut self) {
if let Some(task) = self.task.take() {
self.queue.release_task(task);
}
}
}
let id = task.id;
let mut bomb = Bomb {
task: Some(task),
queue: &mut *self,
};
let res = {
let waker = Task::waker_ref(bomb.task.as_ref().unwrap());
let mut cx = Context::from_waker(&waker);
let stream = unsafe { Pin::new_unchecked(stream) };
stream.poll_next(&mut cx)
};
polled += 1;
match res {
Poll::Pending => {
let task = bomb.task.take().unwrap();
bomb.queue.link(task);
if polled == YIELD_EVERY {
cx.waker().wake_by_ref();
return Poll::Pending;
}
continue;
}
Poll::Ready(None) => {
let task = bomb.task.take().unwrap();
unsafe {
*task.is_done.get() = true;
}
bomb.queue.link(task);
return Poll::Ready(Some((
StreamYield::Finished(FinishedStream { token: id }),
id,
)));
}
Poll::Ready(Some(output)) => {
Task::wake_by_ref(bomb.task.as_ref().unwrap());
let task = bomb.task.take().unwrap();
bomb.queue.link(task);
return Poll::Ready(Some((StreamYield::Item(output), id)));
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<S> Debug for StreamUnordered<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StreamUnordered {{ ... }}")
}
}
impl<S> Drop for StreamUnordered<S> {
fn drop(&mut self) {
unsafe {
while !self.head_all.get_mut().is_null() {
let head = *self.head_all.get_mut();
let task = self.unlink(head);
self.release_task(task);
}
}
}
}
impl<S: Stream> FromIterator<S> for StreamUnordered<S> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = S>,
{
let acc = StreamUnordered::new();
iter.into_iter().fold(acc, |mut acc, item| {
acc.insert(item);
acc
})
}
}
impl<S: Stream + 'static> FusedStream for StreamUnordered<S> {
fn is_terminated(&self) -> bool {
self.is_terminated.load(Relaxed)
}
}
#[cfg(test)]
mod micro {
use super::*;
use futures_util::{stream, stream::StreamExt};
use std::pin::Pin;
#[test]
fn no_starvation() {
let forever0 = Box::pin(stream::iter(vec![0].into_iter().cycle()));
let forever1 = Box::pin(stream::iter(vec![1].into_iter().cycle()));
let two = Box::pin(stream::iter(vec![2].into_iter()));
let mut s = StreamUnordered::new();
let forever0 = s.insert(forever0 as Pin<Box<dyn Stream<Item = i32>>>);
let forever1 = s.insert(forever1 as Pin<Box<dyn Stream<Item = i32>>>);
let two = s.insert(two as Pin<Box<dyn Stream<Item = i32>>>);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
let mut s = rt.block_on(s.take(100).collect::<Vec<_>>()).into_iter();
let mut got_two = false;
let mut got_two_end = false;
while let Some((v, si)) = s.next() {
if let StreamYield::Item(v) = v {
if si == two {
assert_eq!(v, 2);
got_two = true;
} else if si == forever0 {
assert_eq!(v, 0);
} else if si == forever1 {
assert_eq!(v, 1);
} else {
unreachable!("unknown stream {} yielded {}", si, v);
}
} else if si == two {
got_two_end = true;
} else {
unreachable!("unexpected stream end for stream {}", si);
}
}
assert!(got_two, "stream was starved");
assert!(got_two_end, "stream end was not announced");
}
}