use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::Stream;
use futures_util::StreamExt;
use minarrow::{Field, Vec64};
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use crate::models::frames::lightstream_message::LightstreamMessage;
use crate::models::decoders::limits::DecodeLimits;
use crate::models::readers::lightstream::LightstreamReader;
use crate::traits::parallel_transport_reader::SortBehaviour;
const STREAM_CHANNEL_DEPTH: usize = 8;
type StreamItem = io::Result<LightstreamMessage>;
pub struct LightstreamParallelReader {
streams: Vec<mpsc::Receiver<StreamItem>>,
tasks: Vec<JoinHandle<()>>,
stream_count: usize,
sort: SortBehaviour,
cursor: usize,
closed: Vec<bool>,
}
impl LightstreamParallelReader {
pub async fn accept(
listener: &TcpListener,
stream_count: usize,
messages: &[&str],
tables: &[(&str, Vec<Field>)],
sort: SortBehaviour,
limits: Option<DecodeLimits>,
) -> io::Result<Self> {
assert!(stream_count >= 1, "stream_count must be at least 1");
let mut slots: Vec<Option<mpsc::Receiver<StreamItem>>> =
(0..stream_count).map(|_| None).collect();
let mut tasks = Vec::with_capacity(stream_count);
for _ in 0..stream_count {
let (socket, _peer) = listener.accept().await?;
let (mut read_half, _write_half) = socket.into_split();
let mut index_byte = [0u8; 1];
read_half.read_exact(&mut index_byte).await?;
let index = index_byte[0] as usize;
if index >= stream_count {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("connection index {index} out of range for {stream_count} streams"),
));
}
if slots[index].is_some() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("duplicate connection index {index}"),
));
}
let mut reader = LightstreamReader::<Vec64<u8>>::new(read_half, limits);
for name in messages {
reader.register_message(*name);
}
for (name, schema) in tables {
reader.register_table(*name, schema.clone());
}
let (tx, rx) = mpsc::channel(STREAM_CHANNEL_DEPTH);
let task = tokio::spawn(async move {
while let Some(item) = reader.next().await {
match item {
Ok(message) => {
if tx.send(Ok(message)).await.is_err() {
break;
}
}
Err(e) => {
let _ = tx.send(Err(e)).await;
break;
}
}
}
});
slots[index] = Some(rx);
tasks.push(task);
}
let streams: Vec<mpsc::Receiver<StreamItem>> =
slots.into_iter().map(|slot| slot.expect("every connection index filled")).collect();
Ok(Self {
streams,
tasks,
stream_count,
sort,
cursor: 0,
closed: vec![false; stream_count],
})
}
pub fn stream_count(&self) -> usize {
self.stream_count
}
pub async fn read_all(mut self) -> io::Result<Vec<LightstreamMessage>> {
let mut out = Vec::new();
while let Some(item) = self.next().await {
out.push(item?);
}
Ok(out)
}
}
impl Stream for LightstreamParallelReader {
type Item = StreamItem;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
if this.sort == SortBehaviour::Ordered {
let idx = this.cursor % this.stream_count;
return match this.streams[idx].poll_recv(cx) {
Poll::Ready(Some(item)) => {
this.cursor += 1;
Poll::Ready(Some(item))
}
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
};
}
let n = this.stream_count;
let mut any_pending = false;
for offset in 0..n {
let idx = (this.cursor + offset) % n;
if this.closed[idx] {
continue;
}
match this.streams[idx].poll_recv(cx) {
Poll::Ready(Some(item)) => {
this.cursor = (idx + 1) % n;
return Poll::Ready(Some(item));
}
Poll::Ready(None) => this.closed[idx] = true,
Poll::Pending => any_pending = true,
}
}
if any_pending {
Poll::Pending
} else {
Poll::Ready(None)
}
}
}
impl Drop for LightstreamParallelReader {
fn drop(&mut self) {
for task in &self.tasks {
task.abort();
}
}
}