use bytes::Bytes;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot, Mutex, RwLock};
use super::connection::{MultiplexError, MultiplexedConnection};
pub struct PipelinedRequest {
pub id: u64,
pub stream_id: u16,
pub data: Bytes,
pub response_tx: oneshot::Sender<Result<Bytes, MultiplexError>>,
}
pub struct PipelinedClient {
connection: Arc<MultiplexedConnection>,
request_id: AtomicU64,
pending: Arc<RwLock<HashMap<u64, PendingRequest>>>,
max_in_flight: usize,
in_flight: Arc<std::sync::atomic::AtomicUsize>,
}
struct PendingRequest {
stream_id: u16,
response_tx: oneshot::Sender<Result<Bytes, MultiplexError>>,
}
impl PipelinedClient {
pub fn new(connection: Arc<MultiplexedConnection>, max_in_flight: usize) -> Self {
Self {
connection,
request_id: AtomicU64::new(1),
pending: Arc::new(RwLock::new(HashMap::new())),
max_in_flight,
in_flight: Arc::new(std::sync::atomic::AtomicUsize::new(0)),
}
}
pub fn connection(&self) -> &Arc<MultiplexedConnection> {
&self.connection
}
pub fn in_flight_count(&self) -> usize {
self.in_flight.load(Ordering::Relaxed)
}
pub fn can_send(&self) -> bool {
self.in_flight_count() < self.max_in_flight
}
pub async fn send(
&self,
data: Bytes,
) -> Result<oneshot::Receiver<Result<Bytes, MultiplexError>>, MultiplexError> {
if !self.can_send() {
return Err(MultiplexError::SendBufferFull);
}
let stream_id = self.connection.open_stream().await?;
let request_id = self.request_id.fetch_add(1, Ordering::SeqCst);
let (response_tx, response_rx) = oneshot::channel();
{
let mut pending = self.pending.write().await;
pending.insert(
request_id,
PendingRequest {
stream_id,
response_tx,
},
);
}
self.in_flight.fetch_add(1, Ordering::AcqRel);
if let Err(error) = self.connection.send(stream_id, data).await {
self.pending.write().await.remove(&request_id);
self.in_flight.fetch_sub(1, Ordering::AcqRel);
self.connection.rollback_stream_open(stream_id).await;
return Err(error);
}
Ok(response_rx)
}
pub async fn send_batch(
&self,
requests: Vec<Bytes>,
) -> Result<Vec<oneshot::Receiver<Result<Bytes, MultiplexError>>>, MultiplexError> {
let mut receivers = Vec::with_capacity(requests.len());
for data in requests {
let rx = self.send(data).await?;
receivers.push(rx);
}
Ok(receivers)
}
pub async fn process_response(
&self,
stream_id: u16,
data: Bytes,
) -> Result<(), MultiplexError> {
let pending_request = {
let mut pending = self.pending.write().await;
let mut found_id = None;
for (id, req) in pending.iter() {
if req.stream_id == stream_id {
found_id = Some(*id);
break;
}
}
found_id.and_then(|id| pending.remove(&id))
};
if let Some(req) = pending_request {
self.in_flight.fetch_sub(1, Ordering::AcqRel);
let _ = req.response_tx.send(Ok(data));
self.connection.close_stream(stream_id).await?;
}
Ok(())
}
pub async fn process_error(
&self,
stream_id: u16,
error: MultiplexError,
) -> Result<(), MultiplexError> {
let pending_request = {
let mut pending = self.pending.write().await;
let mut found_id = None;
for (id, req) in pending.iter() {
if req.stream_id == stream_id {
found_id = Some(*id);
break;
}
}
found_id.and_then(|id| pending.remove(&id))
};
if let Some(req) = pending_request {
self.in_flight.fetch_sub(1, Ordering::AcqRel);
let _ = req.response_tx.send(Err(error));
}
Ok(())
}
pub async fn cancel_all(&self) {
let mut pending = self.pending.write().await;
for (_, req) in pending.drain() {
let _ = req.response_tx.send(Err(MultiplexError::ConnectionClosed));
}
self.in_flight.store(0, Ordering::Release);
}
pub async fn pending_count(&self) -> usize {
self.pending.read().await.len()
}
}
pub struct RequestPipeline {
client: Arc<PipelinedClient>,
request_tx: mpsc::Sender<PipelinedRequest>,
request_rx: Mutex<mpsc::Receiver<PipelinedRequest>>,
}
impl RequestPipeline {
pub fn new(
connection: Arc<MultiplexedConnection>,
max_in_flight: usize,
buffer_size: usize,
) -> Self {
let (request_tx, request_rx) = mpsc::channel(buffer_size);
let client = Arc::new(PipelinedClient::new(connection, max_in_flight));
Self {
client,
request_tx,
request_rx: Mutex::new(request_rx),
}
}
pub fn client(&self) -> &Arc<PipelinedClient> {
&self.client
}
pub async fn submit(
&self,
data: Bytes,
) -> Result<oneshot::Receiver<Result<Bytes, MultiplexError>>, MultiplexError> {
self.client.send(data).await
}
pub async fn process_incoming(
&self,
stream_id: u16,
data: Bytes,
) -> Result<(), MultiplexError> {
self.client.process_response(stream_id, data).await
}
pub fn in_flight_count(&self) -> usize {
self.client.in_flight_count()
}
pub fn can_accept(&self) -> bool {
self.client.can_send()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_pipelined_client_creation() {
let conn = Arc::new(MultiplexedConnection::new());
let client = PipelinedClient::new(conn, 10);
assert_eq!(client.in_flight_count(), 0);
assert!(client.can_send());
}
#[tokio::test]
async fn test_pipelined_send() {
let conn = Arc::new(MultiplexedConnection::new());
let client = PipelinedClient::new(conn.clone(), 10);
let rx = client.send(Bytes::from("request1")).await.unwrap();
assert_eq!(client.in_flight_count(), 1);
let streams = conn.active_streams().await;
assert!(!streams.is_empty());
let stream_id = streams[0];
client
.process_response(stream_id, Bytes::from("response1"))
.await
.unwrap();
let result = rx.await.unwrap();
assert_eq!(result.unwrap(), Bytes::from("response1"));
assert_eq!(client.in_flight_count(), 0);
}
#[tokio::test]
async fn test_pipelined_batch() {
let conn = Arc::new(MultiplexedConnection::new());
let client = PipelinedClient::new(conn.clone(), 10);
let requests = vec![
Bytes::from("req1"),
Bytes::from("req2"),
Bytes::from("req3"),
];
let receivers = client.send_batch(requests).await.unwrap();
assert_eq!(receivers.len(), 3);
assert_eq!(client.in_flight_count(), 3);
}
#[tokio::test]
async fn test_pipelined_max_in_flight() {
let conn = Arc::new(MultiplexedConnection::new());
let client = PipelinedClient::new(conn, 2);
let _rx1 = client.send(Bytes::from("req1")).await.unwrap();
let _rx2 = client.send(Bytes::from("req2")).await.unwrap();
assert_eq!(client.in_flight_count(), 2);
assert!(!client.can_send());
let result = client.send(Bytes::from("req3")).await;
assert!(matches!(result, Err(MultiplexError::SendBufferFull)));
}
#[tokio::test]
async fn test_pipelined_cancel_all() {
let conn = Arc::new(MultiplexedConnection::new());
let client = PipelinedClient::new(conn, 10);
let rx1 = client.send(Bytes::from("req1")).await.unwrap();
let rx2 = client.send(Bytes::from("req2")).await.unwrap();
client.cancel_all().await;
assert_eq!(client.in_flight_count(), 0);
let result1 = rx1.await.unwrap();
assert!(matches!(result1, Err(MultiplexError::ConnectionClosed)));
let result2 = rx2.await.unwrap();
assert!(matches!(result2, Err(MultiplexError::ConnectionClosed)));
}
#[tokio::test]
async fn test_request_pipeline() {
let conn = Arc::new(MultiplexedConnection::new());
let pipeline = RequestPipeline::new(conn, 10, 100);
assert!(pipeline.can_accept());
assert_eq!(pipeline.in_flight_count(), 0);
}
}