Expand description
§myelin
Define async service APIs as traits, communicate over channels.
The trait definition is the single source of truth. A proc macro generates
the channel plumbing: request/response enums, client stubs, and server
dispatch. Transport and serialization are pluggable — local in-process
channels just move Send structs; cross-boundary transports serialize
transparently.
§Transports
ClientTransport— client side: make a request, get a response.ServerTransport— server side: receive requests, send responses.
Implementations:
transport_tokio(featuretokio) — tokio mpsc + oneshot, local, no serialization.transport_smol(featuresmol) — async-channel mpsc + bounded(1) reply channel, local, no serialization.transport_embassy(featureembassy) — embassy static channels + signals.transport_postcard(featurepostcard) — postcard serialization over any syncRead + Writestream, length-prefix framing. Internally wraps the sync I/O inio::BlockingIoso the async transport stack runs each read/write inline — suitable for stdio-style binaries driven by a trivialBlockOn.
§Async I/O
The stream transport (stream::StreamTransport) operates on
io::AsyncBytesRead / io::AsyncBytesWrite — myelin’s own
runtime-neutral async byte-stream traits (just read_exact,
write_all, flush). Bring your own reader/writer by implementing
those traits, or use one of the feature-gated adapters:
io::BlockingIo— wrap any syncstd::io::Read/Write. The async methods resolve inline; used bytransport_postcardfor stdio transport.io::futures_io(featurefutures-io) — adapt any type bounded byfutures_io::AsyncRead/AsyncWrite. Covers smol’ssmol::Async<T>, async-std, and anything in the futures-io ecosystem.io::tokio_io(featuretokio-io) — adapttokio::io::AsyncRead/AsyncWrite. Independent of thetokiofeature (channels-only transport).
Inside myelin core, shared access to a reader/writer between
concurrent async tasks is mediated by a small single-waiter async
mutex, io::LocalLock. It replaces the old RefCell-based
scheme (unsound across .await) and is cancel-safe.
§Duplex transport
For peers that both call and serve over one byte stream, use
stream::DuplexStreamTransport. It owns one (reader, writer)
pair and vends:
stream::DuplexClientHalf—impl ClientTransportper API that this peer calls into.stream::DuplexServerHalf—impl ServerTransportper API that this peer serves.stream::DuplexPump— arun()future the user’s runtime spawns; it drives the reader and demultiplexes frames into either the slot router (responses) or a per-api_id server inbox (requests).
Wire format inside each framed payload: [u8 kind][u16 api_id LE] [u8 slot_id][codec bytes]. kind is 0 (request) or 1
(response); api_id identifies the API; slot_id is the caller’s
echo-back value. See stream::duplex for details.
Example (sketch, full example in tests/duplex_smol_integration.rs):
use myelin::io::futures_io::{FuturesIoReader, FuturesIoWriter};
use myelin::stream::{DuplexStreamTransport, LengthPrefixed, PostcardCodec};
type Dx<R, W> = DuplexStreamTransport<R, W, LengthPrefixed, PostcardCodec, 8, 512>;
let dx: Dx<_, _> = Dx::new(r, w);
let server = dx.server_half::<u32, u32>(0x0001);
let client = dx.client_half::<u32, u32>(0x0002);
let (pump, _handle) = dx.split();
smol::block_on(async {
// Run pump concurrently with your server loop and client calls.
let _ = futures_lite::future::zip(pump.run(), async { /* ... */ }).await;
});§Cancel Safety
All myelin transports provide the following cancel safety guarantees:
§Cancelling a client call is always safe
A client’s call() future can be dropped at any .await point without
corrupting the transport or leaking state.
-
Dropped before the request is sent: No effect. The request was never enqueued and no server-side resources are consumed.
-
Dropped after the request is sent, before the reply is received: The server will still process the request and produce a response, but that response is discarded. This means the server does wasted work, but there is no protocol corruption, no leaked memory, and no poisoned state. The client can immediately make another call.
§Cancelling a server task affects in-flight clients
If the server task/thread is cancelled or shut down, clients with in-flight requests will observe a transport-specific outcome:
- Tokio: The client receives a
ChannelClosederror (the mpsc/oneshot senders are dropped). - Embassy: The client’s
Signal::wait()will never complete — the client hangs. Avoid cancelling embassy server tasks while clients are in-flight. - PostcardStream: The underlying I/O stream is closed, producing an I/O error on the client side.
§Summary
| Scenario | Result |
|---|---|
| Client cancelled before send | Clean, no effect |
| Client cancelled after send | Server does wasted work, reply discarded |
| Server cancelled | In-flight clients get an error (tokio/postcard) or hang (embassy) |
Re-exports§
pub use block_on::BlockOn;pub use error::CallError;pub use error::TransportResult;pub use transport::ClientTransport;pub use transport::ServerTransport;
Modules§
- block_
on - Blocking executor trait for sync client wrappers.
- error
- Error types and result helpers for myelin service calls.
- io
- Async byte-stream I/O traits owned by myelin.
- stream
- Layered stream transport: Framing × Encoding × ReplyRouting.
- transport
- The core transport traits.
- transport_
embassy - Embassy-based local transport: static
Channelfor requests, per-clientSignalfor replies. - transport_
postcard - Postcard transport over any
Read + Writestream. - transport_
smol - Smol-compatible local transport, mirroring
transport_tokio. - transport_
tokio - Tokio-based local transport:
mpscfor requests,oneshotfor replies.
Macros§
- compose_
service - Compose multiple service APIs into a single multiplexed service.
Attribute Macros§
- service
- Generate channel-API plumbing from an async trait definition.