Coroutine Theory

来源:https://lewissbaker.github.io/2017/09/25/coroutine-theory

Coroutines

Coroutines generalise the operations of a function by separating out some of the steps performed in the Call and Return operations into three extra operations: Suspend, Resume and Destroy.

比其他文章多描述了一个 Destroy 步骤,更准确。

Note that, like the Return operation of a function, a coroutine can only be suspended from within the coroutine itself at well-defined suspend-points.

请注意,就像函数的 Return 操作一样,协程只能在定义好的暂停点处从协程自身内部暂停。

Coroutine activation frames

With coroutines there are some parts of the activation frame that need to be preserved across coroutine suspension and there are some parts that only need to be kept around while the coroutine is executing. For example, the lifetime of a variable with a scope that does not span any coroutine suspend-points can potentially be stored on the stack.

对于协程,activation frame 的某些部分在协程暂停时需要保留,而 activation frame 的某些部分只有在协程执行时才需要保留。例如,不跨越任何协程暂停点的变量可以潜在地存储在堆栈中。

You can logically think of the activation frame of a coroutine as being comprised of two parts: the ‘coroutine frame’ and the ‘stack frame’.

您可以从逻辑上将协程的 activation frame 想象为由两部分组成:“coroutine frame”和“stack frame”。

The ‘coroutine frame’ holds part of the coroutine’s activation frame that persists while the coroutine is suspended and the ‘stack frame’ part only exists while the coroutine is executing and is freed when the coroutine suspends and transfers execution back to the caller/resumer.

“coroutine frame”保留了协程 activation frame 的一部分,该部分在协程被挂起时仍然存在,而“stack frame”部分仅在协程执行时存在,而在协程挂起并将执行转移回调用者/恢复者时则被释放。

The ‘Suspend’ operation

Once the coroutine has been prepared for resumption, the coroutine is considered ‘suspended’.

The coroutine then has the opportunity to execute some additional logic before execution is transferred back to the caller/resumer. This additional logic is given access to a handle to the coroutine-frame that can be used to later resume or destroy it.

协程进入暂停状态之后,执行逻辑返还调用者之前,是有机会做些额外操作的:有个句柄可用,以便恢复或销毁协程。

是不是指 awaiter 的 await_suspend() 接口呢?

The ‘Resume’ operation

When a function wants to resume a coroutine it needs to effectively ‘call’ into the middle of a particular invocation of the function. The way the caller/resumer identifies the particular invocation to resume is by calling the void resume() method on the coroutine-frame handle provided to the corresponding Suspend operation.

恢复协程的方式,是调用 handle.resume(),handle 对象由与之对应的 暂停操作提供。

The ‘Destroy’ operation

The Destroy operation destroys the coroutine frame without resuming execution of the coroutine.

This operation can only be performed on a suspended coroutine.

Similar to the Resume operation, the Destroy operation identifies the particular activation-frame to destroy by calling the void destroy() method on the coroutine-frame handle provided during the corresponding Suspend operation.

The ‘Call’ operation of a coroutine

However, rather than execution only returning to the caller when the function has run to completion, with a coroutine the call operation will instead resume execution of the caller when the coroutine reaches its first suspend-point.

但是,不是执行仅在函数运行完成后才返回到调用方,使用协程时,在协程到达其第一个挂起点时,调用操作将恢复调用方的执行。

此时会返给调用方一个变量哦,这个变量一般都会持有协程的句柄

The ‘Return’ operation of a coroutine

When a coroutine executes a return-statement (co_return according to the TS) operation it stores the return-value somewhere (exactly where this is stored can be customised by the coroutine) and then destructs any in-scope local variables (but not parameters).

The coroutine then has the opportunity to execute some additional logic before transferring execution back to the caller/resumer.

是不是指 promise.return_value() 接口呢?

It is important to note that the return-value passed to the Return operation is not the same as the return-value returned from a Call operation as the return operation may be executed long after the caller resumed from the initial Call operation.

不单单是返回值不同吧?返回类型大概率都不一样啊?

An illustration 插图

Note that the compiler will typically hold the address of the coroutine frame in a separate register to the stack pointer (eg. MSVC stores this in the rbp register).

如何理解“coroutine frame 被存在另一个专用寄存器中”呢? 这个操作意味着什么?

When the coroutine suspends for the first time, a return-value is returned to the caller. This return value often holds a handle to the coroutine-frame that suspended that can be used to later resume it.

调用者之后就能够使用这个返回值(中的句柄)来恢复协程的执行。