Alex Mikhalev
4 years ago
6 changed files with 233 additions and 247 deletions
@ -1,224 +0,0 @@ |
|||||||
use log::trace; |
|
||||||
use std::{ |
|
||||||
any::{Any, TypeId}, |
|
||||||
borrow::Cow, |
|
||||||
collections::HashMap, |
|
||||||
marker::PhantomData, |
|
||||||
}; |
|
||||||
use tokio::sync::{broadcast, mpsc, oneshot}; |
|
||||||
|
|
||||||
use crate::{message::Message, publication::Publication, subscription::Subscription}; |
|
||||||
|
|
||||||
pub enum BrokerError { |
|
||||||
MismatchedType, |
|
||||||
} |
|
||||||
|
|
||||||
pub type BrokerResult<T> = Result<T, BrokerError>; |
|
||||||
|
|
||||||
pub type ErasedSender = Box<dyn Any + Send + Sync>; |
|
||||||
|
|
||||||
fn downcast_sender_ref<T: Message>(erased: &ErasedSender) -> BrokerResult<&broadcast::Sender<T>> { |
|
||||||
(**erased) |
|
||||||
.downcast_ref::<broadcast::Sender<T>>() |
|
||||||
.ok_or(BrokerError::MismatchedType) |
|
||||||
} |
|
||||||
|
|
||||||
pub trait MessageType { |
|
||||||
fn message_type_id(&self) -> TypeId; |
|
||||||
|
|
||||||
fn message_type_name(&self) -> &'static str; |
|
||||||
|
|
||||||
fn create_broadcast_sender(&self) -> ErasedSender; |
|
||||||
} |
|
||||||
|
|
||||||
pub struct BasicMessageType<T> { |
|
||||||
_phantom: PhantomData<T>, |
|
||||||
} |
|
||||||
|
|
||||||
impl<T> Default for BasicMessageType<T> { |
|
||||||
fn default() -> Self { |
|
||||||
BasicMessageType { |
|
||||||
_phantom: PhantomData, |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl<T: Message> MessageType for BasicMessageType<T> { |
|
||||||
fn message_type_id(&self) -> TypeId { |
|
||||||
TypeId::of::<T>() |
|
||||||
} |
|
||||||
|
|
||||||
fn message_type_name(&self) -> &'static str { |
|
||||||
std::any::type_name::<T>() |
|
||||||
} |
|
||||||
|
|
||||||
fn create_broadcast_sender(&self) -> ErasedSender { |
|
||||||
trace!( |
|
||||||
"Creating sender for {} ({:?})", |
|
||||||
std::any::type_name::<T>(), |
|
||||||
MessageType::type_id(self) |
|
||||||
); |
|
||||||
// TODO: configurable queue size (per message?)
|
|
||||||
let (sender, _) = broadcast::channel::<T>(8); |
|
||||||
let sender: ErasedSender = Box::new(sender); |
|
||||||
sender |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub trait SubscribeRequest { |
|
||||||
fn message_type(&self) -> &dyn MessageType; |
|
||||||
|
|
||||||
/// `sender` must be `tokio::sync::broadcast::Sender<T>` where
|
|
||||||
/// `MessageType::get_message_type` returns the `TypeId` of `T`.
|
|
||||||
fn send_subscribe_response(self: Box<Self>, sender: &ErasedSender); |
|
||||||
} |
|
||||||
|
|
||||||
pub type SubscribeRequestBox = Box<dyn SubscribeRequest + Send + Sync>; |
|
||||||
pub type SubscribeRequestSender = mpsc::Sender<SubscribeRequestBox>; |
|
||||||
|
|
||||||
pub struct BasicSubscribeRequest<T> { |
|
||||||
msg_type: BasicMessageType<T>, |
|
||||||
response_tx: oneshot::Sender<BrokerResult<Subscription<T>>>, |
|
||||||
} |
|
||||||
|
|
||||||
impl<T: Message> SubscribeRequest for BasicSubscribeRequest<T> { |
|
||||||
fn message_type(&self) -> &dyn MessageType { |
|
||||||
&self.msg_type |
|
||||||
} |
|
||||||
|
|
||||||
fn send_subscribe_response(self: Box<Self>, sender: &ErasedSender) { |
|
||||||
let subscription = downcast_sender_ref::<T>(sender).map(|sender| { |
|
||||||
let receiver = sender.subscribe(); |
|
||||||
Subscription::new(receiver) |
|
||||||
}); |
|
||||||
let _ = self.response_tx.send(subscription); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl<T: Message> BasicSubscribeRequest<T> { |
|
||||||
pub(crate) fn new() -> (Self, oneshot::Receiver<BrokerResult<Subscription<T>>>) { |
|
||||||
let (response_tx, response_rx) = oneshot::channel(); |
|
||||||
( |
|
||||||
Self { |
|
||||||
msg_type: Default::default(), |
|
||||||
response_tx, |
|
||||||
}, |
|
||||||
response_rx, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub trait AdvertiseRequest { |
|
||||||
fn message_type(&self) -> &dyn MessageType; |
|
||||||
|
|
||||||
/// `sender` must be `tokio::sync::broadcast::Sender<T>` where
|
|
||||||
/// `MessageType::get_message_type` returns the `TypeId` of `T`.
|
|
||||||
fn send_advertise_response(self: Box<Self>, sender: &ErasedSender); |
|
||||||
} |
|
||||||
|
|
||||||
pub type AdvertiseRequestBox = Box<dyn AdvertiseRequest + Send + Sync>; |
|
||||||
pub type AdvertiseRequestSender = mpsc::Sender<AdvertiseRequestBox>; |
|
||||||
|
|
||||||
pub struct BasicAdvertiseRequest<T> { |
|
||||||
msg_type: BasicMessageType<T>, |
|
||||||
response_tx: oneshot::Sender<BrokerResult<Publication<T>>>, |
|
||||||
} |
|
||||||
|
|
||||||
impl<T: Message> AdvertiseRequest for BasicAdvertiseRequest<T> { |
|
||||||
fn message_type(&self) -> &dyn MessageType { |
|
||||||
&self.msg_type |
|
||||||
} |
|
||||||
|
|
||||||
fn send_advertise_response(self: Box<Self>, sender: &ErasedSender) { |
|
||||||
let publication = |
|
||||||
downcast_sender_ref::<T>(sender).map(|sender| Publication::new(sender.clone())); |
|
||||||
let _ = self.response_tx.send(publication); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl<T: Message> BasicAdvertiseRequest<T> { |
|
||||||
pub(crate) fn new() -> (Self, oneshot::Receiver<BrokerResult<Publication<T>>>) { |
|
||||||
let (response_tx, response_rx) = oneshot::channel(); |
|
||||||
( |
|
||||||
Self { |
|
||||||
msg_type: Default::default(), |
|
||||||
response_tx, |
|
||||||
}, |
|
||||||
response_rx, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug)] |
|
||||||
struct TopicEntry { |
|
||||||
message_type_id: TypeId, |
|
||||||
message_type_name: String, |
|
||||||
sender: ErasedSender, |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug)] |
|
||||||
struct Registry { |
|
||||||
topics: HashMap<Cow<'static, str>, TopicEntry>, |
|
||||||
} |
|
||||||
|
|
||||||
impl Default for Registry { |
|
||||||
fn default() -> Self { |
|
||||||
Registry { |
|
||||||
topics: HashMap::new(), |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl Registry { |
|
||||||
fn get_sender_for_type(&mut self, message_type: &dyn MessageType) -> &ErasedSender { |
|
||||||
let type_id = message_type.message_type_id(); |
|
||||||
let type_name = message_type.message_type_name(); |
|
||||||
let topic_entry = self.topics.entry(type_name.into()); |
|
||||||
let topic_entry = topic_entry.or_insert_with(|| TopicEntry { |
|
||||||
message_type_id: type_id, |
|
||||||
message_type_name: type_name.to_string(), |
|
||||||
sender: message_type.create_broadcast_sender(), |
|
||||||
}); |
|
||||||
&topic_entry.sender |
|
||||||
} |
|
||||||
|
|
||||||
fn handle_subscribe(&mut self, subscribe_request: SubscribeRequestBox) { |
|
||||||
let sender = self.get_sender_for_type(subscribe_request.message_type()); |
|
||||||
subscribe_request.send_subscribe_response(sender) |
|
||||||
} |
|
||||||
|
|
||||||
fn handle_advertise(&mut self, advertise_request: AdvertiseRequestBox) { |
|
||||||
let sender = self.get_sender_for_type(advertise_request.message_type()); |
|
||||||
advertise_request.send_advertise_response(sender) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub struct BrokerTask { |
|
||||||
pub(crate) subscribe_rx: mpsc::Receiver<SubscribeRequestBox>, |
|
||||||
pub(crate) advertise_rx: mpsc::Receiver<AdvertiseRequestBox>, |
|
||||||
} |
|
||||||
|
|
||||||
impl BrokerTask { |
|
||||||
pub(crate) async fn run(mut self) { |
|
||||||
trace!("BrokerTask starting"); |
|
||||||
|
|
||||||
let mut registry = Registry::default(); |
|
||||||
|
|
||||||
loop { |
|
||||||
tokio::select! { |
|
||||||
Some(subscribe_req) = self.subscribe_rx.recv() => { |
|
||||||
registry.handle_subscribe(subscribe_req) |
|
||||||
} |
|
||||||
Some(advertise_req) = self.advertise_rx.recv() => { |
|
||||||
registry.handle_advertise(advertise_req) |
|
||||||
} |
|
||||||
else => { |
|
||||||
trace!("no more handles to BrokerTask"); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
trace!("BrokerTask exiting"); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,5 @@ |
|||||||
|
pub enum BrokerError { |
||||||
|
MismatchedType, |
||||||
|
} |
||||||
|
|
||||||
|
pub type BrokerResult<T> = Result<T, BrokerError>; |
@ -0,0 +1,47 @@ |
|||||||
|
use log::trace; |
||||||
|
use tokio::{sync::mpsc, task::JoinHandle}; |
||||||
|
|
||||||
|
mod error; |
||||||
|
mod registry; |
||||||
|
mod request; |
||||||
|
|
||||||
|
pub(crate) use error::{BrokerError, BrokerResult}; |
||||||
|
use registry::Registry; |
||||||
|
pub(crate) use request::{ |
||||||
|
AdvertiseRequest, BrokerRequestBox, BrokerRequestSender, SubscribeRequest, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) struct BrokerTask { |
||||||
|
request_rx: mpsc::Receiver<BrokerRequestBox>, |
||||||
|
} |
||||||
|
|
||||||
|
impl BrokerTask { |
||||||
|
pub(crate) fn start() -> (BrokerRequestSender, JoinHandle<()>) { |
||||||
|
let (request_tx, request_rx) = mpsc::channel(8); |
||||||
|
|
||||||
|
let broker = BrokerTask { request_rx }; |
||||||
|
let join_handle = tokio::spawn(broker.run()); |
||||||
|
|
||||||
|
(request_tx, join_handle) |
||||||
|
} |
||||||
|
|
||||||
|
async fn run(mut self) { |
||||||
|
trace!("BrokerTask starting"); |
||||||
|
|
||||||
|
let mut registry = Registry::default(); |
||||||
|
|
||||||
|
loop { |
||||||
|
tokio::select! { |
||||||
|
Some(request) = self.request_rx.recv() => { |
||||||
|
request.handle_request(&mut registry); |
||||||
|
} |
||||||
|
else => { |
||||||
|
trace!("no more handles to BrokerTask"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
trace!("BrokerTask exiting"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
use crate::Message; |
||||||
|
use log::trace; |
||||||
|
use std::{ |
||||||
|
any::{Any, TypeId}, |
||||||
|
borrow::Cow, |
||||||
|
collections::HashMap, |
||||||
|
marker::PhantomData, |
||||||
|
}; |
||||||
|
use tokio::sync::broadcast; |
||||||
|
use super::{BrokerError, BrokerResult}; |
||||||
|
|
||||||
|
pub trait MessageType { |
||||||
|
fn message_type_id(&self) -> TypeId; |
||||||
|
|
||||||
|
fn message_type_name(&self) -> &'static str; |
||||||
|
|
||||||
|
fn create_broadcast_sender(&self) -> ErasedSender; |
||||||
|
} |
||||||
|
|
||||||
|
pub struct BasicMessageType<T> { |
||||||
|
_phantom: PhantomData<T>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Default for BasicMessageType<T> { |
||||||
|
fn default() -> Self { |
||||||
|
BasicMessageType { |
||||||
|
_phantom: PhantomData, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: Message> MessageType for BasicMessageType<T> { |
||||||
|
fn message_type_id(&self) -> TypeId { |
||||||
|
TypeId::of::<T>() |
||||||
|
} |
||||||
|
|
||||||
|
fn message_type_name(&self) -> &'static str { |
||||||
|
std::any::type_name::<T>() |
||||||
|
} |
||||||
|
|
||||||
|
fn create_broadcast_sender(&self) -> ErasedSender { |
||||||
|
trace!( |
||||||
|
"Creating sender for {} ({:?})", |
||||||
|
std::any::type_name::<T>(), |
||||||
|
MessageType::type_id(self) |
||||||
|
); |
||||||
|
// TODO: configurable queue size (per message?)
|
||||||
|
let (sender, _) = broadcast::channel::<T>(8); |
||||||
|
let sender: ErasedSender = Box::new(sender); |
||||||
|
sender |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub type ErasedSender = Box<dyn Any + Send + Sync>; |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct TopicEntry { |
||||||
|
message_type_id: TypeId, |
||||||
|
message_type_name: String, |
||||||
|
sender: ErasedSender, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct Registry { |
||||||
|
topics: HashMap<Cow<'static, str>, TopicEntry>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Registry { |
||||||
|
fn default() -> Self { |
||||||
|
Registry { |
||||||
|
topics: HashMap::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Registry { |
||||||
|
fn get_erased_sender_for_type(&mut self, message_type: &dyn MessageType) -> &ErasedSender { |
||||||
|
let type_id = message_type.message_type_id(); |
||||||
|
let type_name = message_type.message_type_name(); |
||||||
|
let topic_entry = self.topics.entry(type_name.into()); |
||||||
|
let topic_entry = topic_entry.or_insert_with(|| TopicEntry { |
||||||
|
message_type_id: type_id, |
||||||
|
message_type_name: type_name.to_string(), |
||||||
|
sender: message_type.create_broadcast_sender(), |
||||||
|
}); |
||||||
|
&topic_entry.sender |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_sender_for_type<T: Message>( |
||||||
|
&mut self, |
||||||
|
message_type: &dyn MessageType, |
||||||
|
) -> BrokerResult<&broadcast::Sender<T>> { |
||||||
|
let erased = self.get_erased_sender_for_type(message_type); |
||||||
|
(**erased) |
||||||
|
.downcast_ref::<broadcast::Sender<T>>() |
||||||
|
.ok_or(BrokerError::MismatchedType) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
use tokio::sync::{mpsc, oneshot}; |
||||||
|
|
||||||
|
use super::{ |
||||||
|
registry::{BasicMessageType, Registry}, |
||||||
|
BrokerResult, |
||||||
|
}; |
||||||
|
use crate::{Message, Publication, Subscription}; |
||||||
|
|
||||||
|
pub(crate) trait BrokerRequestInternal { |
||||||
|
fn handle_request(self: Box<Self>, registry: &mut Registry); |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) trait BrokerRequest: BrokerRequestInternal {} |
||||||
|
impl<T: BrokerRequestInternal> BrokerRequest for T {} |
||||||
|
|
||||||
|
pub(crate) type BrokerRequestBox = Box<dyn BrokerRequest + Send + Sync>; |
||||||
|
pub(crate) type BrokerRequestSender = mpsc::Sender<BrokerRequestBox>; |
||||||
|
|
||||||
|
pub(crate) struct SubscribeRequest<T> { |
||||||
|
msg_type: BasicMessageType<T>, |
||||||
|
response_tx: oneshot::Sender<BrokerResult<Subscription<T>>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: Message> BrokerRequestInternal for SubscribeRequest<T> { |
||||||
|
fn handle_request(self: Box<Self>, registry: &mut Registry) { |
||||||
|
let sender = registry.get_sender_for_type::<T>(&self.msg_type); |
||||||
|
let subscription = sender.map(|sender| { |
||||||
|
let receiver = sender.subscribe(); |
||||||
|
Subscription::new(receiver) |
||||||
|
}); |
||||||
|
let _ = self.response_tx.send(subscription); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: Message> SubscribeRequest<T> { |
||||||
|
pub(crate) fn new() -> (Box<Self>, oneshot::Receiver<BrokerResult<Subscription<T>>>) { |
||||||
|
let (response_tx, response_rx) = oneshot::channel(); |
||||||
|
( |
||||||
|
Box::new(Self { |
||||||
|
msg_type: Default::default(), |
||||||
|
response_tx, |
||||||
|
}), |
||||||
|
response_rx, |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) struct AdvertiseRequest<T> { |
||||||
|
msg_type: BasicMessageType<T>, |
||||||
|
response_tx: oneshot::Sender<BrokerResult<Publication<T>>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: Message> BrokerRequestInternal for AdvertiseRequest<T> { |
||||||
|
fn handle_request(self: Box<Self>, registry: &mut Registry) { |
||||||
|
let sender = registry.get_sender_for_type::<T>(&self.msg_type); |
||||||
|
let publication = sender.map(|sender| Publication::new(sender.clone())); |
||||||
|
let _ = self.response_tx.send(publication); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T: Message> AdvertiseRequest<T> { |
||||||
|
pub(crate) fn new() -> (Box<Self>, oneshot::Receiver<BrokerResult<Publication<T>>>) { |
||||||
|
let (response_tx, response_rx) = oneshot::channel(); |
||||||
|
( |
||||||
|
Box::new(Self { |
||||||
|
msg_type: Default::default(), |
||||||
|
response_tx, |
||||||
|
}), |
||||||
|
response_rx, |
||||||
|
) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue