Open nrc opened 2 years ago
Just leaving a list of previously discussed ideas:
#[global_executor]
or global existentials defined by external cratestask::Context
task::Context
generic over it's environment Context<T = ()>
The most promising direction at the moment would be: "with
clauses". This project is still getting started, but if successful would enable authoring a general mechanism by which a runtime can be constructed, passed, and overwritten. An introduction blog post for this is currently in the works, and it's expected this will become a sub-group under the async foundations WG.
Though that doesn't mean that we can't do any work while with
clauses are being researched: it'd be great to have a survey which covers all known spawn
methods and scheduling techniques across runtimes. To my knowledge this does not exist yet, and is a requisite for any interface design.
with
clauses themselves don't solve the interop problem, we still need an API for executors.
with
clauses themselves don't solve the interop problem, we still need an API for executors.
That is true, but their design greatly will inform what an executor API would look like. It doesn't seem fruitful to start designing a generalized async runtime APIs before we have clarity if and how with
clauses will integrate into the language.
That's why I'm recommending we start by doing a survey to gather requirements. So once we have more clarity on with
clauses, we're actually able to start designing and experimenting.
I feel like with
is important for some issues, but orthogonal to others. Obviously with
clauses would affect how executors are passed around and interacted with, but they seem mostly orthogonal to the question of how an interface should be specified. In other words, I think the tasks of designing with
clauses and designing an API for executors are mostly parallelisable.
I encourage anyone thinking about designing an async abstraction to read this blog post about structured concurrency and the problems with spawn-like interfaces:
https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/
And then check out the direction standard C++ is heading:
Disclaimer: I am one of the authors of P2300, but not the blog post.
I have written up a draft for the APIs of executors in #13 , feel free to have a look at it.
I also agree that "with clauses" is the best way to plug in an executor, it would enable to user to add trait bound to specify the capabilities they need.
A key goal is to be able to easily switch between executors. Currently, there is no standardised interface for executors, so how a program interacts with an executor is unique to each executor. On the bright side, most executors offer similar APIs (e.g., a spawn function). However, there are differences in the details and some constraints which are not reflected simply by function signatures (e.g., Tokio's requirement that all async code is within the context of an executor).
One approach would be to define a Spawn or Executor trait in std. In addition, there must be a way to 'plug in' the executor, since executors tend to be somewhat global, rather than passed around. We can follow follow the global allocator pattern, but this is not a complete solution since some programs require multiple executors.
Open issues