paladin/channel/
mod.rs

1//! Generic channel behavior for distributed (inter-process) channels.
2//!
3//! It includes a bit more complexity than the traditional
4//! [mpsc](std::sync::mpsc::channel) channel for the following reasons:
5//! - It supports a notion of message acknowledgement.
6//! - It supports a notion of resource release.
7//! - Rather than returning a tuple of `(sender, receiver)`, it breaks each into
8//!   separate methods. This is because generally senders and receivers are
9//!   usually instantiated in separate process, as the channel is meant to
10//!   facilitate inter process communication. This avoids instantiating
11//!   unnecessary resources when only one is needed.
12
13use std::{
14    pin::Pin,
15    task::{Context, Poll},
16};
17
18use anyhow::Result;
19use async_trait::async_trait;
20use futures::Stream;
21use pin_project::{pin_project, pinned_drop};
22
23use crate::{acker::Acker, queue::Publisher, serializer::Serializable};
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum ChannelType {
27    ExactlyOnce,
28    Broadcast,
29}
30
31/// Generic channel behavior for distributed (inter-process) channels.
32///
33/// It includes a bit more complexity than the traditional
34/// [mpsc](std::sync::mpsc::channel) channel for the following reasons:
35/// - It supports a notion of message acknowledgement.
36/// - It supports a notion of resource release.
37/// - Rather than returning a tuple of `(sender, receiver)`, it breaks each into
38///   separate methods.
39/// This is because generally senders and receivers are usually instantiated in
40/// separate process, as the channel is meant to facilitate inter process
41/// communication. This avoids instantiating unnecessary resources when only one
42/// is needed.
43#[async_trait]
44pub trait Channel {
45    type Sender<'a, T: Serializable + 'a>: Publisher<T>;
46    type Acker: Acker;
47    type Receiver<'a, T: Serializable + 'a>: Stream<Item = (T, Self::Acker)>;
48
49    async fn close(&self) -> Result<()>;
50
51    /// Acquire the sender side of the channel.
52    async fn sender<'a, T: Serializable + 'a>(&self) -> Result<Self::Sender<'a, T>>;
53
54    /// Acquire the receiver side of the channel.
55    async fn receiver<'a, T: Serializable + 'a>(&self) -> Result<Self::Receiver<'a, T>>;
56
57    /// Mark the channel for release.
58    fn release(&self);
59}
60
61/// Behavior for issuing new channels and retrieving existing channels.
62///
63/// Implementations should take care to ensure that the same channel is returned
64/// for a given identifier, allocating a new channel only when necessary.
65#[async_trait]
66pub trait ChannelFactory {
67    type Channel: Channel;
68
69    /// Retrieve an existing channel. An identifier is provided when a channel
70    /// is issued.
71    async fn get(&self, identifier: String, channel_type: ChannelType) -> Result<Self::Channel>;
72
73    /// Issue a new channel. An identifier is returned which can be used to
74    /// retrieve the channel later in some other process.
75    async fn issue(&self, channel_type: ChannelType) -> Result<(String, Self::Channel)>;
76}
77
78/// Guard a channel and embed a particular pipe in the lease guard.
79/// A single pipe is embedded, as the guard is meant to be held by a single end
80/// of the channel. The lease guard will release the channel when it is dropped.
81///
82/// [`LeaseGuard`] implements [`Stream`] where the pipe is a [`Stream`], and can
83/// be used as a [`Stream`] directly.
84#[pin_project(PinnedDrop)]
85pub struct LeaseGuard<C: Channel, Pipe> {
86    #[pin]
87    pipe: Pipe,
88    channel: Option<C>,
89}
90
91/// Implement [`Stream`] for [`LeaseGuard`] where the pipe is a [`Stream`].
92impl<C: Channel, Pipe: Stream> Stream for LeaseGuard<C, Pipe> {
93    type Item = Pipe::Item;
94
95    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
96        self.project().pipe.poll_next(cx)
97    }
98}
99
100impl<C: Channel, Pipe> std::ops::Deref for LeaseGuard<C, Pipe> {
101    type Target = Pipe;
102
103    fn deref(&self) -> &Self::Target {
104        &self.pipe
105    }
106}
107
108impl<C: Channel, Pipe> std::ops::DerefMut for LeaseGuard<C, Pipe> {
109    fn deref_mut(&mut self) -> &mut Self::Target {
110        &mut self.pipe
111    }
112}
113
114impl<C: Channel, Pipe> LeaseGuard<C, Pipe> {
115    pub fn new(channel: C, pipe: Pipe) -> Self {
116        Self {
117            pipe,
118            channel: Some(channel),
119        }
120    }
121}
122
123#[pinned_drop]
124impl<C: Channel, Pipe> PinnedDrop for LeaseGuard<C, Pipe> {
125    fn drop(self: Pin<&mut Self>) {
126        let this = self.project();
127        if let Some(channel) = this.channel.take() {
128            channel.release();
129        }
130    }
131}
132
133pub mod coordinated_channel;
134pub mod queue;