use std::{
ops::ControlFlow,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll},
};
use async_selector::pollable::Pollable;
use bytes::Bytes;
use crate::{
StreamId,
codec::encoder::EncoderItem,
global_pool::GlobalPool,
header::ControlFlags,
stream::{FinState, SharedStreamState},
};
pub(crate) struct StreamUpdates {
pub(super) id: StreamId,
pub(super) syn: bool,
pub(super) state: Arc<Mutex<SharedStreamState>>,
}
impl<'a> Pollable<'a, (), GlobalPool> for StreamUpdates {
type Progress = (StreamUpdate, FinState);
fn poll_progress(
self: Pin<&mut Self>,
_: &'a (),
global: &mut GlobalPool,
cx: &mut Context<'_>,
) -> Poll<ControlFlow<Option<Self::Progress>, Self::Progress>> {
let this = self.get_mut();
let mut update = StreamUpdate {
id: this.id,
window_update: 0,
data: Default::default(),
flags: ControlFlags::default(),
};
let mut is_pending = true;
let mut guard = this.state.lock().unwrap();
if let Poll::Ready((window_update, fin_read)) = guard.inbound.poll_update(global) {
update.window_update = window_update;
update.flags.fin_read = fin_read;
is_pending = false;
}
if let Poll::Ready((data, fin_write)) = guard.outbound.poll_update() {
update.data = data;
update.flags.fin_write = fin_write;
is_pending = false;
}
if is_pending {
guard.updates_poller.register(cx.waker());
return Poll::Pending;
}
let inbound_fin = guard.inbound.fin_state();
let outbound_fin = guard.outbound.fin_state();
drop(guard);
update.flags.syn = std::mem::take(&mut this.syn);
let fin_state = inbound_fin.and(outbound_fin);
let update = if fin_state.sent {
ControlFlow::Break(Some((update, fin_state)))
} else {
ControlFlow::Continue((update, fin_state))
};
Poll::Ready(update)
}
}
pub struct StreamUpdate {
pub id: StreamId,
pub window_update: u32,
pub data: Bytes,
pub flags: ControlFlags,
}
impl From<StreamUpdate> for EncoderItem {
fn from(value: StreamUpdate) -> Self {
if value.data.is_empty() {
EncoderItem::new_window_update(value.id, value.flags, value.window_update)
} else if value.window_update > 0 {
EncoderItem::new_window_update_and_data(
value.id,
value.flags,
value.window_update,
value.data,
)
} else {
EncoderItem::new_data(value.id, value.flags, value.data)
}
}
}