use core::fmt;
use std::sync::{Arc, PoisonError};
use crate::sync::Mutex;
use crate::engine::Family;
use crate::net::ConnectionId;
pub(crate) const WAKE_REPLY: u32 = 1;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PostFail {
Full,
Stopped,
}
pub(crate) trait Waker: Send + Sync + 'static {
fn post(&self, connection: ConnectionId, token: u32, value: u64) -> Result<(), PostFail>;
}
pub(crate) enum SlotState<P> {
Idle,
Waiting(u64),
Completed(u64, P),
Dropped(u64),
}
pub(crate) struct DeferSlot<P> {
pub(crate) state: Mutex<SlotState<P>>,
pub(crate) waker: Arc<dyn Waker>,
pub(crate) connection: ConnectionId,
}
impl<P> DeferSlot<P> {
pub(crate) fn new(waker: Arc<dyn Waker>, connection: ConnectionId) -> Self {
DeferSlot {
state: Mutex::new(SlotState::Idle),
waker,
connection,
}
}
pub(crate) fn lock(&self) -> crate::sync::MutexGuard<'_, SlotState<P>> {
self.state.lock().unwrap_or_else(PoisonError::into_inner)
}
}
pub(crate) struct Link<P> {
pub(crate) slot: Arc<DeferSlot<P>>,
pub(crate) generation: u64,
}
pub struct Reply<F: Family> {
pub(crate) parts: F::Parts,
pub(crate) link: Option<Link<F::Parts>>,
pub(crate) deferred: bool,
}
impl<F: Family> Reply<F> {
pub(crate) fn new(parts: F::Parts) -> Self {
Reply {
parts,
link: None,
deferred: false,
}
}
pub fn parts(&self) -> &F::Parts {
&self.parts
}
pub fn parts_mut(&mut self) -> &mut F::Parts {
&mut self.parts
}
pub fn defer(&mut self) -> ReplyToken<F> {
let parts = core::mem::take(&mut self.parts);
let inner = match self.link.take() {
Some(link) if !self.deferred => {
*link.slot.lock() = SlotState::Waiting(link.generation);
self.deferred = true;
Some(TokenInner {
slot: link.slot,
generation: link.generation,
})
}
other => {
self.link = other;
None
}
};
ReplyToken {
reply: Some(Reply::new(parts)),
inner,
}
}
}
impl<F: Family> fmt::Debug for Reply<F>
where
F::Parts: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Reply")
.field("parts", &self.parts)
.field("deferred", &self.deferred)
.finish()
}
}
struct TokenInner<P> {
slot: Arc<DeferSlot<P>>,
generation: u64,
}
macro_rules! define_token {
($($default:tt)*) => {
pub struct ReplyToken<F: Family $($default)*> {
reply: Option<Reply<F>>,
inner: Option<TokenInner<F::Parts>>,
}
#[non_exhaustive]
pub enum CompleteError<F: Family $($default)*> {
Expired(ReplyToken<F>),
Busy(ReplyToken<F>),
}
};
}
#[cfg(feature = "http1")]
define_token!(= crate::http::HttpFamily);
#[cfg(not(feature = "http1"))]
define_token!();
impl<F: Family> ReplyToken<F> {
pub fn reply(&mut self) -> &mut Reply<F> {
self.reply
.get_or_insert_with(|| Reply::new(F::Parts::default()))
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
match &self.inner {
None => true,
Some(inner) => {
!matches!(*inner.slot.lock(), SlotState::Waiting(g) if g == inner.generation)
}
}
}
pub fn complete(mut self) -> Result<(), CompleteError<F>> {
let Some(inner) = self.inner.take() else {
return Err(CompleteError::Expired(self));
};
let parts = self.reply.take().map(|r| r.parts).unwrap_or_default();
{
let mut state = inner.slot.lock();
if !matches!(*state, SlotState::Waiting(g) if g == inner.generation) {
drop(state);
self.reply = Some(Reply::new(parts));
self.inner = Some(inner);
return Err(CompleteError::Expired(self));
}
*state = SlotState::Completed(inner.generation, parts);
}
match inner
.slot
.waker
.post(inner.slot.connection, WAKE_REPLY, inner.generation)
{
Ok(()) => Ok(()),
Err(fail) => {
let mut state = inner.slot.lock();
let parts =
match core::mem::replace(&mut *state, SlotState::Waiting(inner.generation)) {
SlotState::Completed(_, p) => p,
other => {
*state = other;
F::Parts::default()
}
};
if fail == PostFail::Stopped {
*state = SlotState::Idle;
}
drop(state);
self.reply = Some(Reply::new(parts));
self.inner = Some(inner);
Err(match fail {
PostFail::Full => CompleteError::Busy(self),
PostFail::Stopped => CompleteError::Expired(self),
})
}
}
}
}
impl<F: Family> Drop for ReplyToken<F> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let mut state = inner.slot.lock();
if matches!(*state, SlotState::Waiting(g) if g == inner.generation) {
*state = SlotState::Dropped(inner.generation);
drop(state);
let _ = inner
.slot
.waker
.post(inner.slot.connection, WAKE_REPLY, inner.generation);
}
}
}
}
impl<F: Family> fmt::Debug for ReplyToken<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReplyToken")
.field("cancelled", &self.is_cancelled())
.finish()
}
}
impl<F: Family> fmt::Debug for CompleteError<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompleteError::Expired(_) => f.write_str("Expired"),
CompleteError::Busy(_) => f.write_str("Busy"),
}
}
}
impl<F: Family> fmt::Display for CompleteError<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompleteError::Expired(_) => f.write_str(
"the reply came too late: the connection was already answered or closed",
),
CompleteError::Busy(_) => {
f.write_str("the worker's completion queue is full: retry or drop the token")
}
}
}
}
impl<F: Family> std::error::Error for CompleteError<F> {}