Expand description
§asansio
This library contains the async/await state machine for the sans-io design pattern. See sans I/O for network protocols documentation to familiar with the concept. Writing network protocol without performing I/O operations means creating a state machine. Manually creating a state machine could be a tedious process. As Rust async/await concept is an actual state machine with implicit states created during compilation, this library is an experiment with using async/await to provide such state machine automatically. Let’s check if this is good solution to the problem.
This crate could be used also for non network protocol cases, everywhere there is a need for creating a state machine.
This is no_std crate and it doesn’t allocate on the heap.
§Usage
You can provide async iface trait that can handle all SansIo handling. This way you can use single protocol defined in async way with std or async runtime.
This is the example of std interface and client:
use asansio::Io;
use asansio::IoHandle;
use asansio::Sans;
use std::pin::Pin;
pub trait Iface {
async fn recv(&mut self) -> Option<usize>;
async fn send(&mut self, value: usize) -> Option<()>;
}
async fn run(mut iface: impl Iface) -> Option<()> {
loop {
let value = iface.recv().await?;
iface.send(value + 1).await?;
}
}
type Request = Option<usize>;
type Response = Option<usize>;
struct IfaceStd {
response: Option<usize>,
sans: Sans<Request, Response>,
}
impl Iface for IfaceStd {
async fn recv(&mut self) -> Option<usize> {
if let Some(response) = self.response.take() {
return Some(response);
}
self.sans.handle(None).await.unwrap()
}
async fn send(&mut self, value: usize) -> Option<()> {
self.response = self.sans.handle(Some(value)).await.unwrap();
self.response.map(|_| ())
}
}
struct ClientStd {
io: Io<Request, Response>,
handle: Option<IoHandle<Request, Response, Box<dyn Future<Output = ()>>>>,
}
impl ClientStd {
fn new() -> Option<Self> {
let (sans, io) = asansio::new::<Request, Response>();
let proto = Box::pin({
async move {
run(IfaceStd { response: None, sans }).await.unwrap_or(());
}
}) as Pin<Box<dyn Future<Output = ()>>>;
let (handle, request) = io.start(proto).ok().flatten()?;
assert_eq!(request, None);
let handle = Some(handle);
Some(Self { io, handle })
}
fn call(&mut self, value: usize) -> Option<usize> {
let handle = self.handle.take().unwrap();
let (handle, request) = self.io.handle(handle, Some(value)).unwrap()?;
self.handle = Some(handle);
request
}
}
let mut client = ClientStd::new().unwrap();
assert_eq!(client.call(0), Some(1));
assert_eq!(client.call(2), Some(3));
assert_eq!(client.call(8), Some(9));This crate divides a problem into two parts. The first Sans takes care of the state machine
independent of the I/O and the second Io is responsible with I/O communication. There are
two types to manage them: the Io and the Sans, which are constructed by the new function.
These two parts communicate using Request and Respond types, which are defined by the user
(for real scenarios they could be enums).
Sans starts communicating with Io using Sans::handle and providing the initial Request;
it returns the Response from the Io. Io starts sans task by using Io::start which
returns (IoHandle, Request) from Sans. The later communication is done using
Sans::handle and Io::handle.
Possible design for the implementing protocol is to create an async interface trait, which
Sans will use as an interface to the Io. Application which uses async runtime can provide
implementation of this trait using runtime features and use Sans task directly . The one
which uses only std should implement the interface trait and use asansio crate for
communicating between that interface and main loop. See a similar design in
examples.
§Safety
The crate uses unsafe parts for preparing a proper Waker with internal Channel<Request, Response>. Safety is guaranteed by consuming the latest IoHandle and that you cannot mix
Request or Response types (it is guaranteed by generic types of Io, IoHandle and
Sans. Request and Response types are consumed by values.
Structs§
Enums§
Functions§
- new
- Creates a two parts: Sans and Io for the specified Request and Response.