use std::{
pin::Pin,
sync::{Arc, RwLock},
task::{Context, Poll},
};
use futures::{Stream, stream::FusedStream};
use log::trace;
use crate::fork::Fork;
pub struct CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
pub(crate) fork: Arc<RwLock<Fork<BaseStream>>>,
pub id: usize,
}
impl<BaseStream> From<Fork<BaseStream>> for CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
fn from(mut fork: Fork<BaseStream>) -> Self {
let id = fork
.clone_registry
.register()
.expect("Failed to register initial clone");
Self {
id,
fork: Arc::new(RwLock::new(fork)),
}
}
}
impl<BaseStream> Clone for CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
fn clone(&self) -> Self {
let mut fork = self.fork.write().expect("Fork lock poisoned during clone");
let clone_id = fork
.clone_registry
.register()
.expect("Failed to register clone - clone limit exceeded");
drop(fork);
Self {
fork: self.fork.clone(),
id: clone_id,
}
}
}
impl<BaseStream> Stream for CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
type Item = BaseStream::Item;
fn poll_next(self: Pin<&mut Self>, current_task: &mut Context) -> Poll<Option<Self::Item>> {
trace!("Polling next item for clone {}.", self.id);
let waker = current_task.waker();
let mut fork = self
.fork
.write()
.expect("Fork lock poisoned during poll_next");
fork.poll_clone(self.id, waker)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let fork = self
.fork
.read()
.expect("Fork lock poisoned during size_hint");
let (lower, upper) = fork.size_hint();
let n_cached = fork.remaining_queued_items(self.id);
drop(fork);
(lower + n_cached, upper.map(|u| u + n_cached))
}
}
impl<BaseStream> FusedStream for CloneStream<BaseStream>
where
BaseStream: FusedStream<Item: Clone>,
{
fn is_terminated(&self) -> bool {
let fork = self
.fork
.read()
.expect("Fork lock poisoned during is_terminated");
fork.is_terminated() && fork.remaining_queued_items(self.id) == 0
}
}
impl<BaseStream> Drop for CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
fn drop(&mut self) {
if let Ok(mut fork) = self.fork.try_write() {
fork.unregister(self.id);
} else {
log::warn!(
"Failed to acquire lock during clone drop for clone {}",
self.id
);
}
}
}
impl<BaseStream> CloneStream<BaseStream>
where
BaseStream: Stream<Item: Clone>,
{
#[must_use]
pub fn n_queued_items(&self) -> usize {
trace!("Getting the number of queued items for clone {}.", self.id);
self.fork
.read()
.expect("Fork lock poisoned during n_queued_items")
.remaining_queued_items(self.id)
}
}