How are async functions implemented?
To understand this, I am reading the transcript for Explore Swift performance talk given by @John_McCall.
When he explains how async functions work:
func awaitAll(tasks: [Task<Int, Never>]) async -> [Int] { var results = [Int]() for task in tasks { results.append(await task.value) } return results }
At one point, he refers to some local functions, which confuses me.
So far, we’ve only been talking about synchronous functions.
What about async functions? The central idea with async functions is that C threads are a precious resource, and holding on to a C thread just to block is not making good use of that resource.
As a result, async functions are implemented in two special ways: First, they keep their local state on a separate stack from the C stack, and second, they’re actually split into multiple functions at runtime.
So, let’s look at an example async function.
There’s one potential suspension point, await, in this function.
All of these local functions have uses that cross that suspension point, so they can’t be saved on the C stack.
We just talked about how sync functions allocate their local memory on the C stack by subtracting from the stack pointer.
Async functions conceptually work the same way, except they don’t allocate out of a large, contiguous stack.
Instead, async tasks hold on to one or more slabs of memory.
When an async function wants to allocate memory on the async stack, it asks the task for memory.
What are those local functions in the above context?
Are they the multiple functions below?
As a result, async functions are implemented in two special ways: First, they keep their local state on a separate stack from the C stack, and second, they’re actually split into multiple functions at runtime.
Can anyone clarify?
Update: local functions should be local variables, see the answer and confirmation below.