use super::envelope::IncomingEnvelope;
use super::promise::{PromiseResult, ResultSender};
use super::session::SessionInner;
use super::{Error, Message, schema};
use crate::LogId;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Weak};
use std::time::Instant;
#[derive(Clone)]
pub(super) struct OperationKey(Arc<()>);
impl OperationKey {
pub(super) fn new() -> Self {
Self(Arc::new(()))
}
}
impl PartialEq for OperationKey {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for OperationKey {}
impl Hash for OperationKey {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.0).hash(state);
}
}
pub(super) struct PendingOperation {
pub(super) deadline: Instant,
pub(super) sender: ResultSender,
pub(super) log_id: Option<LogId>,
}
impl PendingOperation {
pub(super) fn complete_response(
self,
now: Instant,
retain: impl FnOnce() -> Result<IncomingEnvelope, Error>,
) -> Result<(), Error> {
if now >= self.deadline {
self.fail(Error::Timeout, now);
} else {
assert!(self.sender.response, "only requests accept peer answers");
match retain() {
Ok(message) => {
let _ = self.sender.send(Ok(PromiseResult::Response(message)));
}
Err(error) => {
if self.sender.send(Err(error.clone())).is_ok() {
return Err(error);
}
}
}
}
Ok(())
}
pub(super) fn fail(self, error: Error, now: Instant) {
let error = if now >= self.deadline {
Error::Timeout
} else {
error
};
if matches!(error, Error::Timeout) {
let kind = if self.sender.response {
"request"
} else {
"reply"
};
match self.log_id {
Some(id) => tracing::debug!("{} {} timed out", kind, id),
None => tracing::debug!("{} timed out before sending", kind),
}
}
let _ = self.sender.send(Err(error));
}
}
pub(super) struct OutgoingMessage {
pub(super) body: OutgoingBody,
pub(super) operation: OperationHandle,
#[cfg(any(test, feature = "fuzz"))]
pub(super) deadline: Instant,
}
pub(super) enum OutgoingBody {
Request(Message),
Reply {
id: u64,
result: Result<Message, schema::Error>,
},
}
pub(super) struct OperationHandle {
pub(super) session: Weak<SessionInner>,
pub(super) key: OperationKey,
}
impl OperationHandle {
pub(super) fn record_write(&self, result: Result<(), Error>) {
if let Some(session) = self.session.upgrade() {
session.record_write(&self.key, result);
}
}
#[cfg(any(test, feature = "fuzz"))]
pub(super) fn record_response(self, result: Result<Message, schema::Error>) {
if let Some(session) = self.session.upgrade() {
session.record_response(&self.key, result.map_err(Error::Remote));
}
}
}