Asyncing qaul.net and libraries

Message ID
DKIM signature
Download raw message
Dear everyone,

over the last few months we've been pretty busy writing various
libraries for the qaul.net ecosystem, throughout the application stack.
In this time the question of async came up several times, and every time
we have postponed the descision to later, when we're less time

We have now reached a point where most of the application stack exists
and we can gauge quite well how much effort a switch will be.  There are
good reasons to change to an async threading model, especially when it
comes to interval events in both libqaul as well as ratman.  The
question is that if we already have async internals if it would make
sense make our API also async.

So, here are our options:

1. We make all internals of libqaul and ratman async, but don't change
   the interfaces.  When sending something from an endpoint in ratman we
   need to block in a task, which would defeat some of the performance
   benifits of doing async, but keeps our interfaces nice and tidy.

2. We make internals async, but also the endpoints.  Write operations
   don't need to be blocked on, reads can be done with streaming future
   iterators.  If we want to wrap around a library we need to explicitly
   write a C shim that exposes the functions in a way that calls "await"
   on them too. This might be problematic because I don't know how
   async-std awaiting stuff would work when called from a C++ lib.

3. (I'm not sure this is technically feasable?) We async the internals
   of libqaul and ratman, stick with a single async interface and then
   wrap all functions in a way that spawns a new task on an existing
   runtime, then awaits them in the task, and calls a function pointer
   provided by the API function into C, to make the API completely

   Two potiontial problems with that:

   1. Since we're not blocking on a task and have to await in the event
      loop, we can't just pin a task to our thread, meaning we have to
      do C/C++ to Rust thread intercom, which I'm not sure if it might
      have some weird side efects.
   2. Having a non-blocking API in C might actually be a real pain in
      the ass, especially when you consider that you'll have to write
      all code to be entirely callback based, with no real utility the
      language gives you to make that less bad.

4. We make the interfaces async, but do so by adding a new AsyncEndpoint
   interface.  This way we can implement the async one in Rust if we
   can, but implement the non async one in C++ where we don't have
   access to the event loop.  This wouldn't really work for the libqaul
   API though, which we might also want to make accessible from C++.

Looking at this now I'm kinda leaning towards option 2), which is maybe
a worthwile tradeoff, provided that we can get the async_std
task::block_on(...) overhead to a minimum and really pin the task to our
local thread.

I'm still interested in your thoughts!

~ spacekookie