//! This module provides interface to wrap a stackful synchronous code into an
//! asynchronous command loop.
//!
//! **NOTE** This module documentation should be viewed as a continuation of
//! [the `drone_core` documentation](drone_core::proc_loop).
//!
//! To provide an example, imagine a C library for FAT32 file system. Here is
//! how it could be wrapped:
//!
//! ```
//! # #![feature(const_fn_fn_ptr_basics)]
//! # #![feature(naked_functions)]
//! # #![feature(never_type)]
//! use core::{future::Future, pin::Pin, slice};
//! use drone_core::ffi::{c_char, CString};
//! use drone_cortexm::{
//! proc_loop::{self, Context as _, ProcLoop, Sess as _},
//! sv,
//! };
//! use futures::prelude::*;
//!
//! use drone_cortexm::sv::{SwitchBackService, SwitchContextService};
//!
//! // Stackful fibers need a supervisor.
//! sv::pool! {
//! pool => SERVICES;
//! supervisor => pub Sv;
//! services => {
//! // These services are required for stackful fibers.
//! SwitchContextService;
//! SwitchBackService;
//! }
//! }
//!
//! // Here is the library API.
//! extern "C" {
//! fn f_read(name: *const c_char, buf: *mut u8, count: u32) -> u32;
//! fn f_write(name: *const c_char, buf: *const u8, count: u32) -> u32;
//! }
//!
//! // The library is expecting to be linked with the following two function.
//!
//! #[no_mangle]
//! pub extern "C" fn disk_read(buf: *mut u8, sector: u32, count: u32) -> u32 {
//! // We need to recreate the Yielder with correct type parameters.
//! let yielder = unsafe { proc_loop::Yielder::<Sv, FatfsRes>::new() };
//! // Redirect the request to the command loop. This call is blocking.
//! let req_res = yielder.req(Req::DiskRead { buf, sector, count });
//! // The result variant must correspond to the request variant.
//! unsafe { req_res.disk_read }
//! }
//!
//! #[no_mangle]
//! pub extern "C" fn disk_write(buf: *const u8, sector: u32, count: u32) -> u32 {
//! let yielder = unsafe { proc_loop::Yielder::<Sv, FatfsRes>::new() };
//! let req_res = yielder.req(Req::DiskWrite { buf, sector, count });
//! // The result variant must correspond to the request variant.
//! unsafe { req_res.disk_write }
//! }
//!
//! // We need to map the two functions above to the corresponding functions below.
//!
//! pub async fn disk_read_async(buf: &mut [u8], sector: u32) -> u32 {
//! // Serve `disk_read` asynchronously.
//! unimplemented!()
//! }
//!
//! pub async fn disk_write_async(buf: &[u8], sector: u32) -> u32 {
//! // Serve `disk_write` asynchronously.
//! unimplemented!()
//! }
//!
//! // All possible commands. We can use only `'static` lifetimes here. That is
//! // why we use `*const str`, `*const [u8]`, `*mut [u8]` instead of `&str`,
//! // `&[u8]`, `&mut [u8]`.
//! pub enum Cmd {
//! Read { name: *const str, buf: *mut [u8] },
//! Write { name: *const str, buf: *const [u8] },
//! }
//!
//! // Results for each of the commands above.
//! pub union CmdRes {
//! pub read: u32,
//! pub write: u32,
//! }
//!
//! // All possible requests used by `disk_read` and `disk_write` functions above.
//! pub enum Req {
//! DiskRead { buf: *mut u8, sector: u32, count: u32 },
//! DiskWrite { buf: *const u8, sector: u32, count: u32 },
//! }
//!
//! // Results for each of the requests above.
//! pub union ReqRes {
//! pub disk_read: u32,
//! pub disk_write: u32,
//! }
//!
//! // The use of raw pointers requires this.
//! unsafe impl Send for Cmd {}
//! unsafe impl Send for Req {}
//!
//! // This type will never be instantiated. It is used only to define associated
//! // items with `ProcLoop` trait.
//! pub struct FatfsRes;
//!
//! // This is the type that implements the high-level API of our library.
//! pub struct FatfsSess<'sess>(&'sess mut proc_loop::Fiber<Sv, FatfsRes>);
//!
//! impl ProcLoop for FatfsRes {
//! type Cmd = Cmd;
//! type CmdRes = CmdRes;
//! type Context = proc_loop::Yielder<Sv, FatfsRes>;
//! type Req = Req;
//! type ReqRes = ReqRes;
//!
//! const STACK_SIZE: usize = 0x800;
//!
//! fn run_cmd(cmd: Cmd, _context: Self::Context) -> CmdRes {
//! match cmd {
//! Cmd::Read { name, buf } => {
//! // Rebind lifetimes for the raw pointers.
//! let name = unsafe { &*buf };
//! let buf = unsafe { &mut *buf };
//! // Call the library function synchronously. This will block.
//! let read = unsafe {
//! f_read(
//! CString::new(name).unwrap().as_ptr(),
//! buf.as_mut_ptr(),
//! buf.len() as u32,
//! )
//! };
//! // The result variant must correspond to the command variant.
//! CmdRes { read }
//! }
//! Cmd::Write { name, buf } => {
//! let name = unsafe { &*buf };
//! let buf = unsafe { &*buf };
//! let write = unsafe {
//! f_write(
//! CString::new(name).unwrap().as_ptr(),
//! buf.as_ptr(),
//! buf.len() as u32,
//! )
//! };
//! // The result variant must correspond to the command variant.
//! CmdRes { write }
//! }
//! }
//! }
//! }
//!
//! impl proc_loop::Sess for FatfsSess<'_> {
//! type Error = !;
//! type Fiber = proc_loop::Fiber<Sv, FatfsRes>;
//! type ProcLoop = FatfsRes;
//!
//! fn fib(&mut self) -> Pin<&mut Self::Fiber> {
//! Pin::new(self.0)
//! }
//!
//! fn run_req(
//! &mut self,
//! req: <Self::ProcLoop as ProcLoop>::Req,
//! ) -> Pin<
//! Box<
//! dyn Future<Output = Result<<Self::ProcLoop as ProcLoop>::ReqRes, Self::Error>>
//! + Send
//! + '_,
//! >,
//! > {
//! match req {
//! Req::DiskRead { buf, sector, count } => {
//! let slice = unsafe { slice::from_raw_parts_mut(buf, count as usize) };
//! Box::pin(disk_read_async(slice, sector).map(|disk_read| {
//! // The result variant must correspond to the request variant.
//! Ok(ReqRes { disk_read })
//! }))
//! }
//! Req::DiskWrite { buf, sector, count } => {
//! let slice = unsafe { slice::from_raw_parts(buf, count as usize) };
//! Box::pin(disk_write_async(slice, sector).map(|disk_write| {
//! // The result variant must correspond to the request variant.
//! Ok(ReqRes { disk_write })
//! }))
//! }
//! }
//! }
//! }
//!
//! // The high-level API to our library.
//! impl FatfsSess<'_> {
//! pub async fn read<'a>(&'a mut self, name: &'a str, buf: &'a mut [u8]) -> Result<u32, !> {
//! let res = self.cmd(Cmd::Read { name, buf }).await?;
//! // The result variant must correspond to the command variant.
//! Ok(unsafe { res.read })
//! }
//!
//! pub async fn write<'a>(&'a mut self, name: &'a str, buf: &'a [u8]) -> Result<u32, !> {
//! let res = self.cmd(Cmd::Write { name, buf }).await?;
//! // The result variant must correspond to the command variant.
//! Ok(unsafe { res.write })
//! }
//! }
//!
//! // Here is how we use the defined command loop in asynchronous context.
//! # fn main() {
//! async {
//! let mut fatfs_proc = proc_loop::Fiber::<Sv, FatfsRes>::new();
//! let mut fatfs_sess = FatfsSess(&mut fatfs_proc);
//! let mut buf = [0; 10];
//! fatfs_sess.read("file1.txt", &mut buf).await?;
//! fatfs_sess.write("file2.txt", b"hello there!\n").await?;
//! Ok::<(), !>(())
//! };
//! # }
//! ```
pub use *;
use crate::;
use Pin;
type InnerYielder<Sv, T> = Yielder>;
type InnerFiber<Sv, T> = FiberProc, >;
type InnerIn<T> = ;
type InnerOut<T> = ;
type CmdLoop<Sv, T> =
fn !;
/// A wrapper for [`fib::FiberProc`] that runs the command loop `T`.
where
Sv: ,
Sv: ,
T: ;
/// Yielder for [`Fiber`]'s [`fib::FiberProc`].
where
Sv: ,
Sv: ,
T: ;