arcnmx / qapi-rs

QEMU QMP and Guest Agent protocol for Rust
MIT License
46 stars 12 forks source link

Experimenting with async-std #5

Open elmarco opened 5 years ago

elmarco commented 5 years ago

The future/async stuff is not easy to grasp, I am just playing around.

I have got something sort of working, help & comments are welcome: https://github.com/elmarco/qapi-rs/tree/async/async.

In particular, I am not sure how events should be exposed. I think they should be a Stream, but then it's not obvious to me how another control could "share" the connection and send other commands since a fused stream will own the reference...

arcnmx commented 5 years ago

Yeah, in the tokio-qapi async/await implementation I went back and forth a lot about exactly how I wanted to deal with that, and sharing underlying ownership across the one-way event stream and the command/response parts. What it IIRC currently does is:

  1. Expose EventStream for events that owns the actual connection (or at least the read side of it), and is effectively a mini event loop for qapi. Polling the event stream is required for any commands to be processed
  2. A separate shared dispatcher is used for dispatching command+response futures. It sets up an async channel for each command and communicates them back to the EventStream.
  3. The EventStream filters input from the socket, returning raw events to the Stream consumer, but dispatches responses to specific future backchannels.
  4. I recall a few complications around...
    • QMP/QGA handshake protocol so that the dispatcher isn't returned until a successful connection has been established (futures that return futures!)
    • QAPI OOB commands where only a subset of commands can be sent OOB? Things would be simpler if all commands supported it.
    • the exact behaviour of dropping one end or the other, handling EPIPE scenarios properly and gracefully shutting everything down.

I think my intention for future improvements was to keep a similar approach but try and make things more ergonomic, and isolate the dispatcher logic into something a bit more clean (use an internal Sink for responses rather than using shared memory?). I intended to also look into tower which I think is supposed to be a building block for RPC style interfaces; not sure if it's overkill, too unstable, supports async-std, but worth noting at least. I got tired of the state of async/await and dealing with the futures 0.1 vs 0.3 compat interface so haven't looked into it in a while.