Conduit

Struct Conduit 

Source
pub struct Conduit<T> { /* private fields */ }
Expand description

A conduit for a simplified request-response communication between asynchronous tasks that allows an owner task to listen for requests for a resource T from any number of requester tasks, and to then asynchronously serve such requests.

The Conduit should belong to the owner task, as it allows to listen for and serve the requests. The conduit can spawn any number of linked Retrievers, which the requester tasks may use to asynchronously retrieve the resource T.

§Error handling

The conduit-retriever pair implements a communication method between asynchronous tasks, so it is only natural that communication errors may arise. Suppose two tasks are communicating: one owns a conduit, and the other owns a linked retriever. Requester task then initiates retrieving of the resource T from the owner task. This process is not atomic:

  • the requester task sends a request,
  • the owner task receives the request,
  • the owner task sends a response,
  • the requester task receives the response.

Anywhere in this process one of the two asynchronous tasks may stop existing. Furthermore, even if both tasks remain in existence, the owner task may choose to not listen to incoming requests, or to not serve them (e.g., there is an error producing the resource T).

This implementation chooses a largely error-less approach to dealing with these uncertainties:

  1. The requester task may choose to either wait for the response indefinitely or to deal with lack of response. Another option is to wait for a response within a timeout.
  2. The owner task may choose whether to listen to requests or whether to send responses.

Any breakage of communication caused by either of the tasks exiting is consciously treated as a normal part of the application lifecycle: it does not cause panics and is not logged.

§Example

use strut_sync::Conduit;
use strut_sync::Retriever;
use tokio::sync::oneshot;

#[tokio::main]
async fn main() {
    // Create a conduit and a related retriever
    let conduit: Conduit<String> = Conduit::new();
    let retriever: Retriever<String> = conduit.retriever();

    // Spawn owner task
    let owner = tokio::spawn(async move {
        // Stand-in for a valuable resource
        let resource: String = "valuable_resource".to_string();

        // Await a request
        let sender: oneshot::Sender<String> = conduit.requested().await;

        // Send a response
        sender.send(resource).unwrap();
    });

    // Spawn requester task
    let requester = tokio::spawn(async move {
        // Acquire the resource
        let acquired_resource: String = retriever.request().await.unwrap();

        // Assert it is what is expected
        assert_eq!(&acquired_resource, "valuable_resource");
    });

    requester.await.unwrap();
    owner.await.unwrap();
}

Implementations§

Source§

impl<T> Conduit<T>

Source

pub fn new() -> Self

Creates and returns a new Conduit that may spawn any number of connected Retrievers. The retrievers may be used to request the resource T from the owner of the conduit.

Source

pub fn retriever(&self) -> Retriever<T>

Spawns and returns a Retriever that is linked to this Conduit and can be used to request the resource T from the conduit’s owner. The returned retriever may be cloned and shared among multiple asynchronous tasks.

Source

pub async fn requested(&self) -> Sender<T>

Waits until the resource T is requested from any of the connected Retrievers. Upon first such request, returns the one-off sender through which the resource T should be sent back to whoever requested it.

This method should be repeatedly awaited on by the asynchronous task that owns the resource T. Only one asynchronous task may listen for and serve requests at any given moment, which is the limitation that comes from the nature of the mpsc channels (see mpsc::Receiver::recv).

§Return type

It is notable that this method returns a oneshot::Sender, not an Option of it.

Technically, it is possible to receive None from mpsc::Receiver::recv. This happens iff there are no buffered messages in the mpsc channel and the mpsc channel is closed. The mpsc channel is closed when either all senders are dropped, or when mpsc::Receiver::close is called.

This conduit owns at least one copy of mpsc::Sender (requester_template), so dropping all senders without also dropping this conduit is not possible.

Then, this conduit does not call close and also does not expose ways to call it externally.

Thus, this method takes a calculated risk of unwrapping the Option before returning.

Trait Implementations§

Source§

impl<T: Debug> Debug for Conduit<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T> !Freeze for Conduit<T>

§

impl<T> !RefUnwindSafe for Conduit<T>

§

impl<T> Send for Conduit<T>
where T: Send,

§

impl<T> Sync for Conduit<T>
where T: Send,

§

impl<T> Unpin for Conduit<T>

§

impl<T> UnwindSafe for Conduit<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.