use crate::{credentials, msg::recv};
use core::task::{Context, Poll};
use s2n_quic_core::varint::VarInt;
use std::sync::{Arc, Weak};
use tokio::sync::mpsc;
type Sender = mpsc::Sender<recv::Message>;
type ReceiverChan = mpsc::Receiver<recv::Message>;
type Key = (credentials::Id, VarInt);
type HashMap = flurry::HashMap<Key, Sender>;
pub enum Outcome {
Forwarded,
Created { receiver: Receiver },
}
pub struct Map {
inner: Arc<HashMap>,
next: Option<(Sender, ReceiverChan)>,
channel_size: usize,
}
impl Default for Map {
#[inline]
fn default() -> Self {
Self {
inner: Default::default(),
next: None,
channel_size: 15,
}
}
}
impl Map {
#[inline]
pub fn handle(&mut self, packet: &super::InitialPacket, msg: &mut recv::Message) -> Outcome {
let (sender, receiver) = self
.next
.take()
.unwrap_or_else(|| mpsc::channel(self.channel_size));
let key = (packet.credentials.id, packet.credentials.key_id);
let guard = self.inner.guard();
match self.inner.try_insert(key, sender, &guard) {
Ok(_) => {
drop(guard);
let map = Arc::downgrade(&self.inner);
tracing::trace!(action = "register", credentials = ?&key);
let receiver = ReceiverState {
map,
key,
channel: receiver,
};
let receiver = Receiver(Box::new(receiver));
Outcome::Created { receiver }
}
Err(err) => {
self.next = Some((err.not_inserted, receiver));
tracing::trace!(action = "forward", credentials = ?&key);
if let Err(err) = err.current.try_send(msg.take()) {
match err {
mpsc::error::TrySendError::Closed(_) => {
self.inner.remove(&key, &guard);
tracing::debug!(credentials = ?key, error = "channel_closed");
}
mpsc::error::TrySendError::Full(_) => {
let _ = msg;
tracing::debug!(credentials = ?key, error = "channel_full");
}
}
}
Outcome::Forwarded
}
}
}
}
#[derive(Debug)]
pub struct Receiver(Box<ReceiverState>);
#[derive(Debug)]
struct ReceiverState {
map: Weak<HashMap>,
key: Key,
channel: ReceiverChan,
}
impl Receiver {
#[inline]
pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<Option<recv::Message>> {
self.0.channel.poll_recv(cx)
}
}
impl Drop for Receiver {
#[inline]
fn drop(&mut self) {
if let Some(map) = self.0.map.upgrade() {
tracing::trace!(action = "unregister", credentials = ?&self.0.key);
let _ = map.remove(&self.0.key, &map.guard());
}
}
}