use {
super::EventStream,
crate::{Event, publisher::PublishError},
shaq::broadcast::Producer,
std::{
fmt::Debug,
num::NonZeroUsize,
sync::{Arc, atomic::Ordering},
},
};
pub(crate) struct Publisher<E: Event> {
stream: Arc<EventStream<E>>,
queue_generation: u64,
producer: Option<Producer<E::QueueCell>>,
}
impl<E: Event> Publisher<E> {
pub(crate) fn publish(&mut self, event: &E) -> Result<(), PublishError> {
let Some(producer) = self.producer() else {
return Ok(());
};
let mut write_guard =
unsafe { producer.try_reserve_write() }.ok_or(PublishError::FailedToSend)?;
let write_guard_cell = write_guard.as_mut();
let cell = unsafe { write_guard_cell.assume_init_mut() };
wincode::serialize_into(cell.as_mut(), &event).map_err(PublishError::Serialization)?;
write_guard.publish();
Ok(())
}
pub(crate) fn publish_batch(&mut self, events: &[E]) -> Result<(), PublishError> {
let Ok(event_count) = NonZeroUsize::try_from(events.len()) else {
return Ok(());
};
let Some(producer) = self.producer() else {
return Ok(());
};
let mut write_guard = unsafe { producer.try_reserve_write_batch(event_count) }
.ok_or(PublishError::FailedToSend)?;
for (i, event) in events.iter().enumerate() {
let write_guard_cell = unsafe { write_guard.as_mut(i) };
let cell = unsafe { write_guard_cell.assume_init_mut() };
wincode::serialize_into(cell.as_mut(), &event).map_err(PublishError::Serialization)?;
}
write_guard.publish();
Ok(())
}
#[inline]
fn producer(&mut self) -> Option<&mut Producer<E::QueueCell>> {
if self.stream.queue_generation.load(Ordering::Relaxed) != self.queue_generation {
self.replace_producer();
}
self.producer.as_mut()
}
#[cold]
fn replace_producer(&mut self) {
self.producer = None;
let stream_state = self.stream.state.read().unwrap();
self.queue_generation = self.stream.queue_generation.load(Ordering::Relaxed);
self.producer = stream_state.create_producer();
}
pub(crate) fn is_enabled(&mut self) -> bool {
self.producer().is_some()
}
}
impl<E: Event> Publisher<E> {
pub(super) fn new(
stream: Arc<EventStream<E>>,
queue_generation: u64,
producer: Option<Producer<E::QueueCell>>,
) -> Self {
Self {
stream,
queue_generation,
producer,
}
}
}
impl<E: Event> Debug for Publisher<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Publisher")
.field("producer", &self.producer)
.finish_non_exhaustive()
}
}
impl<E: Event> Drop for Publisher<E> {
fn drop(&mut self) {
self.producer = None;
let mut stream_state = self.stream.state.write().unwrap();
stream_state.remaining_publisher_slots =
stream_state.remaining_publisher_slots.saturating_add(1);
}
}