At SomeCompany we use an internally developed RPC protocol (originally made for Python, but now used in Rust and a to a limited extent in JS) which connects various network services together.
The protocol isn't stricly RPC, it allows some somewhat unusual stuff that make matters complicated, most importantly:
- RPC calls can nest & interleave and they can go in both ways. For example: A client makes an RPC request to a server. The server might first perform another RPC request back to the client, wait for a reply, and only then reply to the original request from the client.
- Besides RPC calls, there is also simple message passing (independent of any outstanding RPC calls), used for example for heartbeat messages etc.
The code that operates the ends of each connection is organized into "tasks" which are single-threaded but asynchronous in the sense that if the task is performing CPU-bound work it's ok for the input requests to accumulate, but otherwise it should generally service requests without delay (ie. low latency is desired).
In a nutshell, right now in the Rust implementation the task is a Tokio task which internally basically only consists of sync code, it takes requests/messages out of an incomming queue and dispatches them onto a (sync) handler object, which has methods for each possible request/message type. Handler can call methods on the task to send RPC replies back when it's appropriate.
The problem with this is that the handler object is awfully stateful.
I was wondering if this could be helped with futures/async/await.
A function performing an RPC request could return a future and the task could .await
it, but then the problem is that the task also needs be able to answer other RPC calls and process messages while awaiting the original call (or even call the same RPC method as part of handling other requests/messages). I guess this could be done with Tokio's LocalSet
if I understand right what it does?
There's also the issue of state - the handlers need to access the task's state (or some subsets of it, which may be overlapping). I guess this could be helped with interior mutability?
I'm generally pretty unsure as to how the API implementing this with futures & awaiting would be structured so I'll be grateful for any advice.
Thanks!