feat(multiplexer): Use single channel for muxer (#133)
This commit is contained in:
parent
ead193f718
commit
19844889e6
6 changed files with 107 additions and 146 deletions
|
|
@ -19,7 +19,7 @@ impl chainsync::Observer<chainsync::HeaderContent> for LoggingObserver {
|
||||||
_content: chainsync::HeaderContent,
|
_content: chainsync::HeaderContent,
|
||||||
tip: &chainsync::Tip,
|
tip: &chainsync::Tip,
|
||||||
) -> Result<chainsync::Continuation, Box<dyn std::error::Error>> {
|
) -> Result<chainsync::Continuation, Box<dyn std::error::Error>> {
|
||||||
log::debug!("asked to roll forward, tip at {:?}", tip);
|
log::info!("asked to roll forward, tip at {:?}", tip);
|
||||||
|
|
||||||
Ok(chainsync::Continuation::Proceed)
|
Ok(chainsync::Continuation::Proceed)
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +96,7 @@ fn do_chainsync(mut channel: ChannelBuffer<StdChannel>) {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
.filter_level(log::LevelFilter::Trace)
|
.filter_level(log::LevelFilter::Info)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
// setup a TCP socket to act as data bearer between our agents and the remote
|
// setup a TCP socket to act as data bearer between our agents and the remote
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,63 @@ impl<C: Channel> From<C> for ChannelBuffer<C> {
|
||||||
ChannelBuffer::new(channel)
|
ChannelBuffer::new(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Channel for VecDeque<Payload> {
|
||||||
|
fn enqueue_chunk(&mut self, chunk: Payload) -> Result<(), ChannelError> {
|
||||||
|
self.push_back(chunk);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dequeue_chunk(&mut self) -> Result<Payload, ChannelError> {
|
||||||
|
let chunk = self.pop_front().ok_or(ChannelError::NotConnected(None))?;
|
||||||
|
Ok(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_messages_in_same_payload() {
|
||||||
|
let mut input = Vec::new();
|
||||||
|
let in_part1 = (1u8, 2u8, 3u8);
|
||||||
|
let in_part2 = (6u8, 5u8, 4u8);
|
||||||
|
|
||||||
|
minicbor::encode(in_part1, &mut input).unwrap();
|
||||||
|
minicbor::encode(in_part2, &mut input).unwrap();
|
||||||
|
|
||||||
|
let mut channel = VecDeque::<Payload>::new();
|
||||||
|
channel.push_back(input);
|
||||||
|
|
||||||
|
let mut buf = ChannelBuffer::new(channel);
|
||||||
|
|
||||||
|
let out_part1 = buf.recv_full_msg::<(u8, u8, u8)>().unwrap();
|
||||||
|
let out_part2 = buf.recv_full_msg::<(u8, u8, u8)>().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(in_part1, out_part1);
|
||||||
|
assert_eq!(in_part2, out_part2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragmented_message_in_multiple_payloads() {
|
||||||
|
let mut input = Vec::new();
|
||||||
|
let msg = (11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8);
|
||||||
|
minicbor::encode(msg, &mut input).unwrap();
|
||||||
|
|
||||||
|
let mut channel = VecDeque::<Payload>::new();
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let chunk = Vec::from(input.drain(0..2).as_slice());
|
||||||
|
channel.push_back(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = ChannelBuffer::new(channel);
|
||||||
|
|
||||||
|
let out_msg = buf.recv_full_msg::<(u8, u8, u8, u8, u8, u8, u8)>().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(msg, out_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ pub mod bearers;
|
||||||
pub mod demux;
|
pub mod demux;
|
||||||
pub mod mux;
|
pub mod mux;
|
||||||
|
|
||||||
use bearers::Bearer;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
mod std;
|
mod std;
|
||||||
|
|
||||||
|
|
@ -13,29 +11,4 @@ pub use crate::std::*;
|
||||||
|
|
||||||
pub type Payload = Vec<u8>;
|
pub type Payload = Vec<u8>;
|
||||||
|
|
||||||
pub struct Multiplexer<I, E>
|
pub type Message = (u16, Payload);
|
||||||
where
|
|
||||||
I: mux::Ingress,
|
|
||||||
E: demux::Egress,
|
|
||||||
{
|
|
||||||
pub muxer: mux::Muxer<I>,
|
|
||||||
pub demuxer: demux::Demuxer<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I, E> Multiplexer<I, E>
|
|
||||||
where
|
|
||||||
I: mux::Ingress,
|
|
||||||
E: demux::Egress,
|
|
||||||
{
|
|
||||||
pub fn new(bearer: Bearer) -> Self {
|
|
||||||
Multiplexer {
|
|
||||||
muxer: mux::Muxer::new(bearer.clone()),
|
|
||||||
demuxer: demux::Demuxer::new(bearer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_channel(&mut self, protocol: u16, ingress: I, egress: E) {
|
|
||||||
self.muxer.register(protocol, ingress);
|
|
||||||
self.demuxer.register(protocol, egress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
use std::{collections::HashMap, time::Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use rand::thread_rng;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bearers::{Bearer, Segment},
|
bearers::{Bearer, Segment},
|
||||||
Payload,
|
Message,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum IngressError {
|
pub enum IngressError {
|
||||||
|
|
@ -18,20 +15,19 @@ pub enum IngressError {
|
||||||
/// To be implemented by any mechanism that allows to submit a payloads from a
|
/// To be implemented by any mechanism that allows to submit a payloads from a
|
||||||
/// particular protocol that need to be muxed by the multiplexer.
|
/// particular protocol that need to be muxed by the multiplexer.
|
||||||
pub trait Ingress {
|
pub trait Ingress {
|
||||||
fn try_recv(&mut self) -> Result<Payload, IngressError>;
|
fn recv_timeout(&mut self, duration: Duration) -> Result<Message, IngressError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message = (u16, Payload);
|
|
||||||
|
|
||||||
pub enum TickOutcome {
|
pub enum TickOutcome {
|
||||||
BearerError(std::io::Error),
|
BearerError(std::io::Error),
|
||||||
|
IngressDisconnected,
|
||||||
Idle,
|
Idle,
|
||||||
Busy,
|
Busy,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Muxer<I> {
|
pub struct Muxer<I> {
|
||||||
bearer: Bearer,
|
bearer: Bearer,
|
||||||
ingress: HashMap<u16, I>,
|
ingress: I,
|
||||||
clock: Instant,
|
clock: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,58 +35,17 @@ impl<I> Muxer<I>
|
||||||
where
|
where
|
||||||
I: Ingress,
|
I: Ingress,
|
||||||
{
|
{
|
||||||
pub fn new(bearer: Bearer) -> Self {
|
pub fn new(bearer: Bearer, ingress: I) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bearer,
|
bearer,
|
||||||
ingress: Default::default(),
|
ingress,
|
||||||
clock: Instant::now(),
|
clock: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register the receiver end of an ingress channel
|
|
||||||
pub fn register(&mut self, id: u16, rx: I) {
|
|
||||||
self.ingress.insert(id, rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a protocol from the ingress
|
|
||||||
///
|
|
||||||
/// Meant to be used after a receive error in a previous tick
|
|
||||||
pub fn deregister(&mut self, id: u16) {
|
|
||||||
self.ingress.remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn randomize_ids(&self) -> Vec<u16> {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
let mut keys: Vec<_> = self.ingress.keys().cloned().collect();
|
|
||||||
keys.shuffle(&mut rng);
|
|
||||||
keys
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select the next segment to be muxed
|
|
||||||
///
|
|
||||||
/// This method iterates over the existing receivers checking for the first
|
|
||||||
/// available message. The order of the checks is random to ensure a fair
|
|
||||||
/// use of the multiplexer amongst all protocols.
|
|
||||||
pub fn select(&mut self) -> Option<Message> {
|
|
||||||
for id in self.randomize_ids() {
|
|
||||||
let rx = self.ingress.get_mut(&id).unwrap();
|
|
||||||
|
|
||||||
match rx.try_recv() {
|
|
||||||
Ok(payload) => return Some((id, payload)),
|
|
||||||
Err(IngressError::Disconnected) => {
|
|
||||||
self.deregister(id);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self) -> TickOutcome {
|
pub fn tick(&mut self) -> TickOutcome {
|
||||||
match self.select() {
|
match self.ingress.recv_timeout(Duration::from_millis(500)) {
|
||||||
Some((id, payload)) => {
|
Ok((id, payload)) => {
|
||||||
let segment = Segment::new(self.clock, id, payload);
|
let segment = Segment::new(self.clock, id, payload);
|
||||||
|
|
||||||
match self.bearer.write_segment(segment) {
|
match self.bearer.write_segment(segment) {
|
||||||
|
|
@ -98,7 +53,8 @@ where
|
||||||
_ => TickOutcome::Busy,
|
_ => TickOutcome::Busy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => TickOutcome::Idle,
|
Err(IngressError::Empty) => TickOutcome::Idle,
|
||||||
|
Err(IngressError::Disconnected) => TickOutcome::IngressDisconnected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
agents::{self, ChannelBuffer},
|
agents::{self, ChannelBuffer},
|
||||||
demux, mux, Payload,
|
bearers::Bearer,
|
||||||
|
demux, mux, Message, Payload,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
mpsc::{channel, Receiver, SendError, Sender, TryRecvError},
|
mpsc::{channel, Receiver, RecvTimeoutError, SendError, Sender},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
thread::{spawn, JoinHandle},
|
thread::{spawn, JoinHandle},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type StdIngress = Receiver<Payload>;
|
pub type StdIngress = Receiver<Message>;
|
||||||
|
|
||||||
impl mux::Ingress for StdIngress {
|
impl mux::Ingress for StdIngress {
|
||||||
fn try_recv(&mut self) -> Result<Payload, mux::IngressError> {
|
fn recv_timeout(&mut self, duration: Duration) -> Result<Message, mux::IngressError> {
|
||||||
match Receiver::try_recv(self) {
|
match Receiver::recv_timeout(self, duration) {
|
||||||
Ok(x) => Ok(x),
|
Ok(x) => Ok(x),
|
||||||
Err(TryRecvError::Disconnected) => Err(mux::IngressError::Disconnected),
|
Err(RecvTimeoutError::Disconnected) => Err(mux::IngressError::Disconnected),
|
||||||
Err(TryRecvError::Empty) => Err(mux::IngressError::Empty),
|
Err(RecvTimeoutError::Timeout) => Err(mux::IngressError::Empty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,16 +37,30 @@ impl demux::Egress for StdEgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type StdPlexer = crate::Multiplexer<StdIngress, StdEgress>;
|
pub struct StdPlexer {
|
||||||
|
pub muxer: mux::Muxer<StdIngress>,
|
||||||
|
pub demuxer: demux::Demuxer<StdEgress>,
|
||||||
|
pub mux_tx: Sender<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
impl StdPlexer {
|
impl StdPlexer {
|
||||||
|
pub fn new(bearer: Bearer) -> Self {
|
||||||
|
let (mux_tx, mux_rx) = channel::<Message>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
muxer: mux::Muxer::new(bearer.clone(), mux_rx),
|
||||||
|
demuxer: demux::Demuxer::new(bearer),
|
||||||
|
mux_tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn use_channel(&mut self, protocol: u16) -> StdChannel {
|
pub fn use_channel(&mut self, protocol: u16) -> StdChannel {
|
||||||
let (demux_tx, demux_rx) = channel::<Payload>();
|
let (demux_tx, demux_rx) = channel::<Payload>();
|
||||||
let (mux_tx, mux_rx) = channel::<Payload>();
|
self.demuxer.register(protocol, demux_tx);
|
||||||
|
|
||||||
self.register_channel(protocol, mux_rx, demux_tx);
|
let mux_tx = self.mux_tx.clone();
|
||||||
|
|
||||||
(mux_tx, demux_rx)
|
(protocol, mux_tx, demux_rx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,12 +71,10 @@ impl mux::Muxer<StdIngress> {
|
||||||
mux::TickOutcome::BearerError(err) => return Err(err),
|
mux::TickOutcome::BearerError(err) => return Err(err),
|
||||||
mux::TickOutcome::Idle => match cancel.is_set() {
|
mux::TickOutcome::Idle => match cancel.is_set() {
|
||||||
true => break Ok(()),
|
true => break Ok(()),
|
||||||
false => {
|
false => (),
|
||||||
// TODO: investigate why std::thread::yield_now() hogs the thread
|
|
||||||
std::thread::sleep(Duration::from_millis(100))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mux::TickOutcome::Busy => (),
|
mux::TickOutcome::Busy => (),
|
||||||
|
mux::TickOutcome::IngressDisconnected => break Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,20 +117,20 @@ impl demux::Demuxer<StdEgress> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type StdChannel = (Sender<Payload>, Receiver<Payload>);
|
pub type StdChannel = (u16, Sender<Message>, Receiver<Payload>);
|
||||||
|
|
||||||
pub type StdChannelBuffer = ChannelBuffer<StdChannel>;
|
pub type StdChannelBuffer = ChannelBuffer<StdChannel>;
|
||||||
|
|
||||||
impl agents::Channel for StdChannel {
|
impl agents::Channel for StdChannel {
|
||||||
fn enqueue_chunk(&mut self, payload: Payload) -> Result<(), agents::ChannelError> {
|
fn enqueue_chunk(&mut self, payload: Payload) -> Result<(), agents::ChannelError> {
|
||||||
match self.0.send(payload) {
|
match self.1.send((self.0, payload)) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(SendError(payload)) => Err(agents::ChannelError::NotConnected(Some(payload))),
|
Err(SendError((_, payload))) => Err(agents::ChannelError::NotConnected(Some(payload))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dequeue_chunk(&mut self) -> Result<Payload, agents::ChannelError> {
|
fn dequeue_chunk(&mut self) -> Result<Payload, agents::ChannelError> {
|
||||||
match self.1.recv() {
|
match self.2.recv() {
|
||||||
Ok(payload) => Ok(payload),
|
Ok(payload) => Ok(payload),
|
||||||
Err(_) => Err(agents::ChannelError::NotConnected(None)),
|
Err(_) => Err(agents::ChannelError::NotConnected(None)),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use pallas_codec::minicbor;
|
||||||
use pallas_multiplexer::{
|
use pallas_multiplexer::{
|
||||||
agents::{Channel, ChannelBuffer},
|
agents::{Channel, ChannelBuffer},
|
||||||
bearers::Bearer,
|
bearers::Bearer,
|
||||||
StdPlexer,
|
Payload, StdPlexer,
|
||||||
};
|
};
|
||||||
use rand::{distributions::Uniform, Rng};
|
use rand::{distributions::Uniform, Rng};
|
||||||
|
|
||||||
|
|
@ -62,44 +62,3 @@ fn one_way_small_sequence_of_payloads() {
|
||||||
assert_eq!(payload, received_payload);
|
assert_eq!(payload, received_payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multiple_messages_in_same_payload() {
|
|
||||||
let mut input = Vec::new();
|
|
||||||
let in_part1 = (1u8, 2u8, 3u8);
|
|
||||||
let in_part2 = (6u8, 5u8, 4u8);
|
|
||||||
|
|
||||||
minicbor::encode(in_part1, &mut input).unwrap();
|
|
||||||
minicbor::encode(in_part2, &mut input).unwrap();
|
|
||||||
|
|
||||||
let channel = std::sync::mpsc::channel();
|
|
||||||
channel.0.send(input).unwrap();
|
|
||||||
|
|
||||||
let mut buf = ChannelBuffer::new(channel);
|
|
||||||
|
|
||||||
let out_part1 = buf.recv_full_msg::<(u8, u8, u8)>().unwrap();
|
|
||||||
let out_part2 = buf.recv_full_msg::<(u8, u8, u8)>().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(in_part1, out_part1);
|
|
||||||
assert_eq!(in_part2, out_part2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn fragmented_message_in_multiple_payloads() {
|
|
||||||
let mut input = Vec::new();
|
|
||||||
let msg = (11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8);
|
|
||||||
minicbor::encode(msg, &mut input).unwrap();
|
|
||||||
|
|
||||||
let channel = std::sync::mpsc::channel();
|
|
||||||
|
|
||||||
while !input.is_empty() {
|
|
||||||
let chunk = Vec::from(input.drain(0..2).as_slice());
|
|
||||||
channel.0.send(chunk).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = ChannelBuffer::new(channel);
|
|
||||||
|
|
||||||
let out_msg = buf.recv_full_msg::<(u8, u8, u8, u8, u8, u8, u8)>().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(msg, out_msg);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue