snaury/coroactors

Back to the drawing board

snaury opened this issue · 1 comments

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 to async def in Python and async 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 the async<T>, which would create its own task
  • I'd like async<T> to have a single entrance via co_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.

Implemented in 144423c