Hello, wise people of Rust!
What's the problem
I have a convoluted use case of async Rust co-operating with a C library with callbacks.
The regular flow of the library, as used from C
- C code calls library (say
setup_device()
). - The library does its magic, but to perform some actions calls callbacks (say, a bunch of
read_register()
,write_register()
events). - Callbacks perform some network
send()
/recv()
(yep, I know, read register via network. Nobody claims people at Microchip are sane). - Far side performs an action and responds (or a packet is lost)
- Upon response or timeout callback returns outcome to the library.
- Library performs many more turnarounds and finally passes the final result to the initial caller.
What I have:
- C library is operational (part of another project)
- It properly links to Rust code (thanks to
cc
crate) - I can call the necessary functions (a hefty
unsafe extern "C" { ... }
) block. - Callbacks are handled by stubs in Rust (they now just log args and return failure), a few
#[unsafe(no_mangle)] pub extern "C" fn XXX(){ ... }
- I have a reactor loop and all the necessary network code.
My idea
Now it is time to stitch it together.
- Have main reactor with
loop { select! { ... }}
. - Have a separate blocking task
async fn lib_requestor_loop(){}
-
connected with main reactor via async mpsc queue
lib_queue
ofenum Cmd{}
, -
each
Cmd
has a one-shot channellib_reply
for reply from the library. -
Dispatched
Cmd
calls blocking the library-
lib callbacks issue
enum NetReq{}
via async mpsc queuenet_rquests
to the main reactor (try_send
is sync, no problem), eachNetReq
has a field with a sync channelnet_reply
forenum NetResult{}
-s (i don't see a sync one-shot implementation anywhere, Tokio's syncone_shot::try_recv()
doesn't block). Callback blocks on recv onnet_reply
.- The main reactor processes the request and reports success/failure back via
net_reply
.
- The main reactor processes the request and reports success/failure back via
-
Recv on
net_reply
unblocks, callback passes the result back to library -
Blocking call to the library returns
-
-
The result is sent back via
lib_reply
channel to the main reactor
-
- Profit!
Do you have any improvements or suggestions for my flow? My main concern is callback-main reactor communication, especially the slippery situation of async reactor and sync callbacks.
Thanks in advance.