use std::sync::Arc;
use plaza::agent::AgentId;
use plaza::session::ConnectionId;
use tokio::time::Instant;
use crate::codec::WireCodec;
use crate::conditioner::Conditioner;
use crate::control::{self, Inbound, ProbeState, DOWN_SEED_FLIP};
use crate::manager::{ConnectionManager, Frame, LinkHandle, SessionClock};
pub struct LinkDriver<ID: AgentId, C: WireCodec> {
manager: Arc<ConnectionManager<ID>>,
conn_id: ConnectionId,
codec: C,
clock: Option<SessionClock>,
link: Arc<LinkHandle>,
up: Conditioner,
down: Conditioner,
probe: ProbeState,
next_probe: Option<Instant>,
pending_inbound: Vec<Frame>,
}
impl<ID: AgentId, C: WireCodec> LinkDriver<ID, C> {
pub fn new(manager: &Arc<ConnectionManager<ID>>, conn_id: ConnectionId, codec: C) -> Option<Self> {
let link = manager.link_handle(conn_id)?;
let queues = manager.queues();
let probe = ProbeState::new(manager.probes());
Some(Self {
manager: Arc::clone(manager),
conn_id,
codec,
clock: manager.clock().cloned(),
link,
up: Conditioner::new(conn_id, queues.conditioner),
down: Conditioner::new(conn_id ^ DOWN_SEED_FLIP, queues.conditioner),
next_probe: probe.first_due(Instant::now()),
probe,
pending_inbound: Vec::new(),
})
}
pub fn inbound(&mut self, frame: Frame, now: Instant) -> Inbound {
if !self.link.impaired() && self.up.is_empty() {
return self.route(frame);
}
let profile = self.link.read().up;
if !self.up.push(frame, &profile, now) {
self.manager.record_link_drop(self.conn_id);
}
Inbound::Consumed
}
pub fn outbound(&mut self, frame: Frame, now: Instant) -> Option<Frame> {
if !self.link.impaired() && self.down.is_empty() {
return Some(frame);
}
let profile = self.link.read().down;
if !self.down.push(frame, &profile, now) {
self.manager.record_link_drop(self.conn_id);
}
None
}
pub fn deadline(&self) -> Option<Instant> {
control::earliest(
self.next_probe,
control::earliest(self.up.next_release(), self.down.next_release()),
)
}
pub fn due(&mut self, now: Instant) -> Vec<Frame> {
let mut owed = Vec::new();
while let Some(frame) = self.down.pop_ready(now) {
owed.push(frame);
}
while let Some(frame) = self.up.pop_ready(now) {
match self.route(frame) {
Inbound::Reply(reply) => owed.push(reply),
Inbound::Forward(frame) => self.pending_inbound.push(frame),
Inbound::Consumed => {}
}
}
if self.next_probe.is_some_and(|at| at <= now) {
owed.push(control::make_probe(&self.codec, &mut self.probe, now));
self.next_probe = self.probe.interval().map(|gap| now + gap);
}
owed
}
pub fn take_forwarded(&mut self) -> Vec<Frame> {
std::mem::take(&mut self.pending_inbound)
}
fn route(&mut self, frame: Frame) -> Inbound {
control::handle_inbound(
frame,
&self.codec,
self.clock.as_ref(),
&mut self.probe,
self.conn_id,
&self.manager,
)
}
}