use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::mpsc;
use zyris_proto::WireMessage;
use crate::async_trait;
use crate::connection::{AcceptOptions, Connection};
use crate::error::{Result, TransportError};
use crate::node::Node;
use crate::transport::{ChannelTransport, Transport, WireSink, WireStream};
pub async fn duplex(dialer: &Node, acceptor: &Node) -> Result<(Connection, Connection)> {
duplex_with(dialer, acceptor, AcceptOptions::default()).await
}
pub async fn duplex_with(
dialer: &Node,
acceptor: &Node,
options: AcceptOptions,
) -> Result<(Connection, Connection)> {
let (dial_transport, accept_transport) = ChannelTransport::pair();
let accept = acceptor.accept(accept_transport, options);
let dial = dialer.connect_over(dial_transport);
tokio::try_join!(dial, accept)
}
#[derive(Debug)]
pub struct Gates {
pub dialer_to_acceptor: Arc<AtomicBool>,
pub acceptor_to_dialer: Arc<AtomicBool>,
pub dialer_sink_fails: Arc<AtomicBool>,
pub acceptor_sink_fails: Arc<AtomicBool>,
pub delivered: Arc<AtomicUsize>,
}
pub async fn gated_duplex(
dialer: &Node,
acceptor: &Node,
options: AcceptOptions,
) -> (Connection, Connection, Gates) {
let (dial_transport, accept_transport, gates) = gated_transport_pair();
let accept = acceptor.accept(accept_transport, options);
let dial = dialer.connect_over(dial_transport);
let (dial_conn, accept_conn) = tokio::join!(dial, accept);
(
dial_conn.expect("dialer handshake failed"),
accept_conn.expect("acceptor handshake failed"),
gates,
)
}
pub fn gated_transport_pair() -> (GatedTransport, GatedTransport, Gates) {
let dialer_to_acceptor = Arc::new(AtomicBool::new(true));
let acceptor_to_dialer = Arc::new(AtomicBool::new(true));
let dialer_sink_fails = Arc::new(AtomicBool::new(false));
let acceptor_sink_fails = Arc::new(AtomicBool::new(false));
let delivered = Arc::new(AtomicUsize::new(0));
let (dialer_tx, dialer_out) = mpsc::unbounded_channel();
let (accept_rx_tx, accept_rx) = mpsc::unbounded_channel();
forward(dialer_out, accept_rx_tx, dialer_to_acceptor.clone(), delivered.clone());
let (acceptor_tx, acceptor_out) = mpsc::unbounded_channel();
let (dialer_rx_tx, dialer_rx) = mpsc::unbounded_channel();
forward(acceptor_out, dialer_rx_tx, acceptor_to_dialer.clone(), delivered.clone());
let gates = Gates {
dialer_to_acceptor,
acceptor_to_dialer,
dialer_sink_fails,
acceptor_sink_fails,
delivered,
};
(
GatedTransport {
sink_tx: dialer_tx,
sink_fails: gates.dialer_sink_fails.clone(),
stream_rx: dialer_rx,
},
GatedTransport {
sink_tx: acceptor_tx,
sink_fails: gates.acceptor_sink_fails.clone(),
stream_rx: accept_rx,
},
gates,
)
}
fn forward(
mut from: mpsc::UnboundedReceiver<WireMessage>,
to: mpsc::UnboundedSender<WireMessage>,
gate: Arc<AtomicBool>,
delivered: Arc<AtomicUsize>,
) {
tokio::spawn(async move {
while let Some(msg) = from.recv().await {
if gate.load(Ordering::Relaxed) {
if to.send(msg).is_ok() {
delivered.fetch_add(1, Ordering::Relaxed);
}
}
}
});
}
pub struct GatedTransport {
sink_tx: mpsc::UnboundedSender<WireMessage>,
sink_fails: Arc<AtomicBool>,
stream_rx: mpsc::UnboundedReceiver<WireMessage>,
}
struct GateSink {
tx: mpsc::UnboundedSender<WireMessage>,
fails: Arc<AtomicBool>,
}
struct GateStream(mpsc::UnboundedReceiver<WireMessage>);
#[async_trait]
impl WireSink for GateSink {
async fn send(&mut self, msg: WireMessage) -> Result<(), TransportError> {
if self.fails.load(Ordering::Relaxed) {
return Err(TransportError::Io("simulated dead socket".into()));
}
self.tx.send(msg).map_err(|_| TransportError::Closed)
}
async fn close(&mut self, _code: u16, _reason: String) -> Result<(), TransportError> {
Ok(())
}
}
#[async_trait]
impl WireStream for GateStream {
async fn next(&mut self) -> Option<Result<WireMessage, TransportError>> {
self.0.recv().await.map(Ok)
}
}
impl Transport for GatedTransport {
fn split(self: Box<Self>) -> (Box<dyn WireSink>, Box<dyn WireStream>) {
(
Box::new(GateSink { tx: self.sink_tx, fails: self.sink_fails }),
Box::new(GateStream(self.stream_rx)),
)
}
}