use crate::mspc::{self, ChannelError};
use std::sync;
pub fn create<E: Send + 'static>(initial_subscribers_capacity: usize) -> Broadcast<E> {
Broadcast::<E>::new(initial_subscribers_capacity)
}
pub struct Broadcast<E: Send + 'static> {
message_receiver: mspc::ReceiveChannel<E>,
message_sender: mspc::SendChannel<E>,
subscribers: sync::Arc<sync::Mutex<Vec<Option<mspc::SendChannel<sync::Arc<E>>>>>>,
}
impl<E: Send + 'static> Clone for Broadcast<E> {
fn clone(&self) -> Self {
Self {
message_receiver: self.message_receiver.clone(),
message_sender: self.message_sender.clone(),
subscribers: self.subscribers.clone(),
}
}
}
impl<E: Send + 'static> Broadcast<E> {
pub(crate) fn new(initial_subscribers_capacity: usize) -> Self {
let (message_sender, message_receiver) = mspc::create::<E>();
return Self {
message_sender,
message_receiver,
subscribers: sync::Arc::new(sync::Mutex::new(Vec::with_capacity(
initial_subscribers_capacity,
))),
};
}
pub fn has_pending_messages(&mut self) -> bool {
!self.message_receiver.is_empty().unwrap()
}
pub fn broadcast(&mut self, item: E) {
self.message_sender
.try_send(item)
.expect("should have delivered message to queue");
self.deliver_pending_messages();
}
pub fn subscribe(&mut self) -> mspc::ReceiveChannel<sync::Arc<E>> {
let (sender, receiver) = mspc::create::<sync::Arc<E>>();
self.add_and_deliver_pending_messages(sender);
receiver
}
fn add_and_deliver_pending_messages(&mut self, sender: mspc::SendChannel<sync::Arc<E>>) {
self.add_subscriber_sender(sender);
self.deliver_pending_messages();
}
fn add_subscriber_sender(&mut self, sender: mspc::SendChannel<sync::Arc<E>>) {
let mut subscribers = self.subscribers.lock().unwrap();
subscribers.push(Some(sender))
}
fn deliver_pending_messages(&mut self) {
let mut subs = self.subscribers.try_lock().unwrap();
if subs.len() == 0 {
return;
}
while let Ok(message) = self.message_receiver.try_receive() {
let message_reference = sync::Arc::new(message);
for sub_slot in subs.iter_mut() {
if let Some(sub) = sub_slot {
match sub.try_send(message_reference.clone()) {
Err(ChannelError::Closed) => {
sub_slot.take();
}
_ => continue,
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::broadcast;
#[test]
fn broadcast_should_cache_pending_messages_when_no_subscribers() {
let mut broadcaster = broadcast::create::<String>(5);
broadcaster.broadcast(String::from("first"));
broadcaster.broadcast(String::from("second"));
assert!(broadcaster.has_pending_messages())
}
#[test]
fn broadcast_should_cache_pending_messages_until_atleast_one_subscriber_joins() {
let mut broadcaster = broadcast::create::<String>(5);
broadcaster.broadcast(String::from("first"));
broadcaster.broadcast(String::from("second"));
assert!(broadcaster.has_pending_messages());
let mut subscriber = broadcaster.subscribe();
assert!(!subscriber.is_empty().unwrap());
assert!(!broadcaster.has_pending_messages());
}
#[test]
fn broadcast_should_immediately_all_pending_messages_to_subscribers() {
let mut broadcaster = broadcast::create::<String>(5);
let mut subscriber = broadcaster.subscribe();
assert!(subscriber.is_empty().unwrap());
broadcaster.broadcast(String::from("first"));
broadcaster.broadcast(String::from("second"));
assert!(!broadcaster.has_pending_messages());
assert!(!subscriber.is_empty().unwrap());
}
#[test]
fn broadcast_new_subscribers_should_miss_already_sent_messages() {
let mut broadcaster = broadcast::create::<String>(5);
let mut subscriber = broadcaster.subscribe();
broadcaster.broadcast(String::from("first"));
broadcaster.broadcast(String::from("second"));
assert!(!broadcaster.has_pending_messages());
assert!(!subscriber.is_empty().unwrap());
let mut subscriber2 = broadcaster.subscribe();
assert!(subscriber2.is_empty().unwrap());
}
#[test]
fn broadcast_new_subscribers_should_miss_already_sent_messages_but_get_new_messages() {
let mut broadcaster = broadcast::create::<String>(5);
let mut subscriber = broadcaster.subscribe();
broadcaster.broadcast(String::from("first"));
assert!(!broadcaster.has_pending_messages());
assert!(!subscriber.is_empty().unwrap());
let mut subscriber2 = broadcaster.subscribe();
broadcaster.broadcast(String::from("second"));
assert!(!subscriber2.is_empty().unwrap());
}
#[test]
fn broadcast_closed_subscriber_still_lets_others_get_message() {
let mut broadcaster = broadcast::create::<String>(5);
let mut subscriber = broadcaster.subscribe();
let mut subscriber2 = broadcaster.subscribe();
subscriber.close();
broadcaster.broadcast(String::from("first"));
broadcaster.broadcast(String::from("second"));
assert!(!subscriber2.is_empty().unwrap());
assert!(matches!(subscriber.is_empty(), Err(_)));
}
}