Initial implementation
This commit is contained in:
parent
4d505f383d
commit
8ecc0acf73
@ -7,11 +7,16 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
futures = "0.3.8"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["sync"]
|
features = ["sync"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.8"
|
||||||
|
|
||||||
[dev-dependencies.tokio]
|
[dev-dependencies.tokio]
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["rt", "macros"]
|
features = ["rt", "rt-multi-thread", "macros"]
|
||||||
|
194
src/broker_task.rs
Normal file
194
src/broker_task.rs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
use log::trace;
|
||||||
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
collections::HashMap,
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
use tokio::sync::{broadcast, mpsc, oneshot};
|
||||||
|
|
||||||
|
use crate::{message::Message, publication::Publication, subscription::Subscription};
|
||||||
|
|
||||||
|
pub type ErasedSender = Box<dyn Any + Send + Sync>;
|
||||||
|
|
||||||
|
pub trait MessageType {
|
||||||
|
fn message_type_id(&self) -> TypeId;
|
||||||
|
|
||||||
|
fn create_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 create_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`.
|
||||||
|
unsafe 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<Subscription<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Message> SubscribeRequest for BasicSubscribeRequest<T> {
|
||||||
|
fn message_type(&self) -> &dyn MessageType {
|
||||||
|
&self.msg_type
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn send_subscribe_response(self: Box<Self>, sender: &ErasedSender) {
|
||||||
|
let sender = &*(&**sender as *const dyn Any as *const broadcast::Sender<T>);
|
||||||
|
// let sender = (**sender).downcast_ref::<broadcast::Sender<T>>().unwrap();
|
||||||
|
let receiver = sender.subscribe();
|
||||||
|
let subscription = Subscription::new(receiver);
|
||||||
|
let _ = self.response_tx.send(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Message> BasicSubscribeRequest<T> {
|
||||||
|
pub(crate) fn new() -> (Self, oneshot::Receiver<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`.
|
||||||
|
unsafe 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<Publication<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Message> AdvertiseRequest for BasicAdvertiseRequest<T> {
|
||||||
|
fn message_type(&self) -> &dyn MessageType {
|
||||||
|
&self.msg_type
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn send_advertise_response(self: Box<Self>, sender: &ErasedSender) {
|
||||||
|
let sender = &*(&**sender as *const dyn Any as *const broadcast::Sender<T>);
|
||||||
|
// let sender = (**sender).downcast_ref::<broadcast::Sender<T>>().unwrap();
|
||||||
|
let publication = Publication::new(sender.clone());
|
||||||
|
let _ = self.response_tx.send(publication);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Message> BasicAdvertiseRequest<T> {
|
||||||
|
pub(crate) fn new() -> (Self, oneshot::Receiver<Publication<T>>) {
|
||||||
|
let (response_tx, response_rx) = oneshot::channel();
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
msg_type: Default::default(),
|
||||||
|
response_tx,
|
||||||
|
},
|
||||||
|
response_rx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Registry {
|
||||||
|
senders: HashMap<TypeId, ErasedSender>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Registry {
|
||||||
|
fn default() -> Self {
|
||||||
|
Registry {
|
||||||
|
senders: 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 sender_entry = self.senders.entry(type_id);
|
||||||
|
sender_entry.or_insert_with(|| message_type.create_sender())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_subscribe(&mut self, subscribe_request: SubscribeRequestBox) {
|
||||||
|
let sender = self.get_sender_for_type(subscribe_request.message_type());
|
||||||
|
unsafe { 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());
|
||||||
|
unsafe { 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trace!("BrokerTask exiting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BrokerTask {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
trace!("BrokerTask dropped");
|
||||||
|
}
|
||||||
|
}
|
114
src/lib.rs
114
src/lib.rs
@ -1,40 +1,79 @@
|
|||||||
|
mod broker_task;
|
||||||
mod message;
|
mod message;
|
||||||
mod publication;
|
mod publication;
|
||||||
mod subscription;
|
mod subscription;
|
||||||
|
|
||||||
|
use broker_task::{
|
||||||
|
AdvertiseRequestSender, BasicAdvertiseRequest, BasicSubscribeRequest, BrokerTask,
|
||||||
|
SubscribeRequestSender,
|
||||||
|
};
|
||||||
pub use message::Message;
|
pub use message::Message;
|
||||||
pub use publication::{Publication, SendError};
|
pub use publication::{Publication, SendError};
|
||||||
pub use subscription::{RecvError, Subscription};
|
pub use subscription::{RecvError, Subscription};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
use futures::executor::block_on;
|
||||||
#[non_exhaustive]
|
use tokio::sync::mpsc;
|
||||||
pub enum SubscribeError {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum AdvertiseError {}
|
pub enum OrsbError {
|
||||||
|
BrokerClosed,
|
||||||
|
NoResponse,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Orsb {}
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Orsb {
|
||||||
|
subscribe_tx: SubscribeRequestSender,
|
||||||
|
advertise_tx: AdvertiseRequestSender,
|
||||||
|
}
|
||||||
|
|
||||||
impl Orsb {
|
impl Orsb {
|
||||||
pub fn new() -> Self {
|
pub fn start_new() -> Self {
|
||||||
todo!()
|
Self::start_new2().0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn subscribe<T: Message>(&mut self) -> Result<Subscription<T>, SubscribeError> {
|
pub(crate) fn start_new2() -> (Self, tokio::task::JoinHandle<()>) {
|
||||||
todo!()
|
let (subscribe_tx, subscribe_rx) = mpsc::channel(8);
|
||||||
|
let (advertise_tx, advertise_rx) = mpsc::channel(8);
|
||||||
|
|
||||||
|
let broker = BrokerTask {
|
||||||
|
subscribe_rx,
|
||||||
|
advertise_rx,
|
||||||
|
};
|
||||||
|
let join_handle = tokio::spawn(broker.run());
|
||||||
|
|
||||||
|
let orsb = Orsb {
|
||||||
|
subscribe_tx,
|
||||||
|
advertise_tx,
|
||||||
|
};
|
||||||
|
|
||||||
|
(orsb, join_handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn advertise<T: Message>(&mut self) -> Result<Publication<T>, AdvertiseError> {
|
pub async fn subscribe<T: Message>(&mut self) -> Result<Subscription<T>, OrsbError> {
|
||||||
todo!()
|
let (subscribe_request, response_rx) = BasicSubscribeRequest::<T>::new();
|
||||||
|
self.subscribe_tx
|
||||||
|
.send(Box::new(subscribe_request))
|
||||||
|
.await
|
||||||
|
.or(Err(OrsbError::BrokerClosed))?;
|
||||||
|
response_rx.await.or(Err(OrsbError::NoResponse))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribe_blocking<T: Message>(&mut self) -> Result<Subscription<T>, SubscribeError> {
|
pub async fn advertise<T: Message>(&mut self) -> Result<Publication<T>, OrsbError> {
|
||||||
todo!()
|
let (advertise_request, response_rx) = BasicAdvertiseRequest::<T>::new();
|
||||||
|
self.advertise_tx
|
||||||
|
.send(Box::new(advertise_request))
|
||||||
|
.await
|
||||||
|
.or(Err(OrsbError::BrokerClosed))?;
|
||||||
|
response_rx.await.or(Err(OrsbError::NoResponse))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn advertise_blocking<T: Message>(&mut self) -> Result<Publication<T>, AdvertiseError> {
|
pub fn subscribe_blocking<T: Message>(&mut self) -> Result<Subscription<T>, OrsbError> {
|
||||||
todo!()
|
block_on(self.subscribe::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advertise_blocking<T: Message>(&mut self) -> Result<Publication<T>, OrsbError> {
|
||||||
|
block_on(self.advertise::<T>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,20 +84,49 @@ mod test {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct TestMsg(u8);
|
struct TestMsg(u8);
|
||||||
|
|
||||||
#[test]
|
fn init_logger() {
|
||||||
fn test_sync() {
|
let _ = env_logger::try_init();
|
||||||
let mut orsb = Orsb::new();
|
}
|
||||||
|
|
||||||
let mut sub = orsb.subscribe_blocking::<TestMsg>().unwrap();
|
#[tokio::test]
|
||||||
let mut publ = orsb.advertise_blocking::<TestMsg>().unwrap();
|
async fn test_sync() {
|
||||||
|
init_logger();
|
||||||
|
|
||||||
|
let mut orsb = Orsb::start_new();
|
||||||
|
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let mut sub = orsb.subscribe_blocking::<TestMsg>().unwrap();
|
||||||
|
let mut publ = orsb.advertise_blocking::<TestMsg>().unwrap();
|
||||||
|
|
||||||
|
publ.send(TestMsg(10)).unwrap();
|
||||||
|
publ.send(TestMsg(20)).unwrap();
|
||||||
|
publ.send(TestMsg(30)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(sub.try_recv(), Ok(TestMsg(10)));
|
||||||
|
assert_eq!(sub.try_recv(), Ok(TestMsg(20)));
|
||||||
|
assert_eq!(sub.try_recv(), Ok(TestMsg(30)));
|
||||||
|
assert_eq!(sub.try_recv(), Err(RecvError::Empty));
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async() {
|
||||||
|
init_logger();
|
||||||
|
|
||||||
|
let mut orsb = Orsb::start_new();
|
||||||
|
|
||||||
|
let mut sub = orsb.subscribe::<TestMsg>().await.unwrap();
|
||||||
|
let mut publ = orsb.advertise::<TestMsg>().await.unwrap();
|
||||||
|
|
||||||
publ.send(TestMsg(10)).unwrap();
|
publ.send(TestMsg(10)).unwrap();
|
||||||
publ.send(TestMsg(20)).unwrap();
|
publ.send(TestMsg(20)).unwrap();
|
||||||
publ.send(TestMsg(30)).unwrap();
|
publ.send(TestMsg(30)).unwrap();
|
||||||
|
|
||||||
assert_eq!(sub.recv_blocking(), Ok(TestMsg(10)));
|
assert_eq!(sub.recv().await, Ok(TestMsg(10)));
|
||||||
assert_eq!(sub.recv_blocking(), Ok(TestMsg(20)));
|
assert_eq!(sub.recv().await, Ok(TestMsg(20)));
|
||||||
assert_eq!(sub.recv_blocking(), Ok(TestMsg(30)));
|
assert_eq!(sub.recv().await, Ok(TestMsg(30)));
|
||||||
assert_eq!(sub.try_recv(), Err(RecvError::Empty));
|
assert_eq!(sub.try_recv(), Err(RecvError::Empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
pub trait Message: 'static + Clone + Send + Sync {}
|
use std::any::Any;
|
||||||
|
|
||||||
|
pub trait Message: 'static + Any + Clone + Send + Sync {}
|
||||||
|
|
||||||
impl<T> Message for T where T: 'static + Clone + Send + Sync {}
|
impl<T> Message for T where T: 'static + Clone + Send + Sync {}
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
use std::marker::PhantomData;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum SendError {
|
pub enum SendError {}
|
||||||
NoListeners,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Publication<T> {
|
pub struct Publication<T> {
|
||||||
_phantom: PhantomData<T>,
|
sender: broadcast::Sender<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Message> Publication<T> {
|
impl<T: Message> Publication<T> {
|
||||||
pub fn send(&mut self, message: T) -> Result<(), SendError> {
|
pub(crate) fn new(sender: broadcast::Sender<T>) -> Self {
|
||||||
let _ = message;
|
Publication { sender }
|
||||||
todo!()
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self, message: T) -> Result<usize, SendError> {
|
||||||
|
match self.sender.send(message) {
|
||||||
|
Ok(subscribers) => Ok(subscribers),
|
||||||
|
Err(_) => Ok(0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::marker::PhantomData;
|
use futures::executor::block_on;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
use crate::message::Message;
|
use crate::message::Message;
|
||||||
|
|
||||||
@ -10,20 +11,46 @@ pub enum RecvError {
|
|||||||
Lagged,
|
Lagged,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<broadcast::error::RecvError> for RecvError {
|
||||||
|
fn from(err: broadcast::error::RecvError) -> Self {
|
||||||
|
match err {
|
||||||
|
broadcast::error::RecvError::Closed => RecvError::Closed,
|
||||||
|
broadcast::error::RecvError::Lagged(_) => RecvError::Lagged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<broadcast::error::TryRecvError> for RecvError {
|
||||||
|
fn from(err: broadcast::error::TryRecvError) -> Self {
|
||||||
|
match err {
|
||||||
|
broadcast::error::TryRecvError::Empty => RecvError::Empty,
|
||||||
|
broadcast::error::TryRecvError::Closed => RecvError::Closed,
|
||||||
|
broadcast::error::TryRecvError::Lagged(_) => RecvError::Lagged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Subscription<T> {
|
pub struct Subscription<T> {
|
||||||
_phantom: PhantomData<T>,
|
receiver: broadcast::Receiver<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Subscription<T> {
|
||||||
|
pub(crate) fn new(receiver: broadcast::Receiver<T>) -> Self {
|
||||||
|
Subscription { receiver }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Message> Subscription<T> {
|
impl<T: Message> Subscription<T> {
|
||||||
pub async fn recv(&mut self) -> Result<T, RecvError> {
|
pub async fn recv(&mut self) -> Result<T, RecvError> {
|
||||||
todo!()
|
Ok(self.receiver.recv().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_blocking(&mut self) -> Result<T, RecvError> {
|
pub fn recv_blocking(&mut self) -> Result<T, RecvError> {
|
||||||
todo!()
|
block_on(self.recv())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_recv(&mut self) -> Result<T, RecvError> {
|
pub fn try_recv(&mut self) -> Result<T, RecvError> {
|
||||||
todo!()
|
Ok(self.receiver.try_recv()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user