Expand description
Adapter bridging crate::Transport to tokio’s
[AsyncRead] + [AsyncWrite] contracts.
The mmdvm crate’s tokio shell requires transports that implement
[tokio::io::AsyncRead] and [tokio::io::AsyncWrite]. The
crate::Transport trait exposes ergonomic async fn read /
async fn write methods, which are incompatible at the trait-object
level. This adapter converts the async fn interface into the
poll-based interface tokio uses internally.
§Implementation strategy
A pump task owns the inner transport and serializes reads and
writes via [tokio::select!]. The adapter communicates with the
pump via two mpsc channels:
- Write channel (
adapter → pump): byte buffers to write. - Read channel (
pump → adapter): byte buffers read from the transport, oneVec<u8>percrate::Transport::readcall.
The pump task’s select! interleaves read and write operations on
the same T, so a pending read never blocks an outgoing write (and
vice versa). This mirrors the serialization that
[tokio::io::split] provides for types that support an explicit
half-split, without requiring T to support one.
MmdvmTransportAdapter::into_inner closes the write channel,
awaits the pump task’s [JoinHandle], and recovers the inner T
that the pump returned on clean exit.
§Thread-affinity (macOS Bluetooth)
The pump task is spawned with [tokio::task::spawn_local] so it
runs on the same OS thread as the calling [tokio::task::LocalSet].
This is required for crate::transport::BluetoothTransport on
macOS: IOBluetooth’s RFCOMM channel callbacks are dispatched to the
CFRunLoop of the thread that opened the channel (typically the
main thread, before the tokio runtime starts). Pumping that runloop
from a worker thread is a no-op — the callbacks never deliver data
into the pipe that BluetoothTransport::read waits on. By keeping
the pump on the same thread, every bt_pump_runloop() call drains
pending callbacks where they actually live.
Callers must therefore construct this adapter from inside a
[tokio::task::LocalSet]. For the REPL/TUI, the top-level
run_repl future is launched via LocalSet::block_on, satisfying
this requirement transparently.
Structs§
- Mmdvm
Transport Adapter - Adapter that presents a
crate::Transportas a tokio [AsyncRead] + [AsyncWrite] +Send+Unpinduplex stream.