Back to the drawing board
snaury opened this issue · 1 comments
snaury commented
I don't think I like current design:
- I don't like
actor<T>
, functions are not actors, actors are bigger than a single function - I don't like how heavyweight current context switching is, coroutines should have very little state
- I don't like to
co_await context()
at the start of every function, even tests look ugly as a result - Tests with use coroutine local values show how weird this "one set of locals before co_await, different locals after co_await"
After getting more inspiration from both Swift and Python, here's what I'd like instead:
- I'd like coroutines to just be lazy start coroutines, very lightweight, without any need for a separate
task<T>
- I'd like these coroutines to be marked
async<T>
, similar toasync def
in Python andasync
functions in Swift - I'd like these coroutines to have an associated "task", which doesn't change as long as it's a single logical thread of execution
- I'd like this task to be extremely liteweight, i.e. there shouldn't be any extra allocations when
async<T>
is a pure coroutine, without any actor context, etc.- This task could be inline in the awaiter from non-async to async coroutines, so it doesn't live "on the heap", and it's lifetime is bound to the first
async<T>
lifetime - The ability to detach async calls may throw a wrench into this, but solvable by using something like
task::detached(async<T>)
static method, which would create a new detached coroutine, then await on theasync<T>
, which would create its own task
- This task could be inline in the awaiter from non-async to async coroutines, so it doesn't live "on the heap", and it's lifetime is bound to the first
- I'd like
async<T>
to have a single entrance viaco_await
and exit via its awaiter, and I'd like it to be as simple as possible, i.e. just return to a continuation that awaited us (optionally restoring context first) - I'd like current task and context to just inherit naturally, i.e. one async to another should be a direct symmetric transfer, no change in current task or anything
- Current stop token, current context, current task local values, will all be properties of the task
- Task group will naturally create a task for each added awaitable, and this new task will naturally inherit parent's task locals, etc.
- Task will need to unmount from thread-locals when awaiting non-task-aware awaitables, and remount when awaiter wrapper resumes (along with a switch to current task's actor context)
- We wouldn't need async promise to inherit from any intrusive data structures, because those can move to "stack locals", e.g. as part of an awaiter
This way I hope to simlify the design, and make actors and async coroutines more orthogonal to each other.