Coroutine

Coroutine

A routine that can also be suspended at some point and resumed from that point when control returns.

  • The state of a coroutine consists of:
    • an execution location, starting at the beginning of the coroutine and remembered at each suspend.
    • an execution state holding the data created by the code the coroutine is executing. each coroutine has its own stack, containing its local variables and those of any routines it calls.
    • an execution status—active or inactive or terminated—which changes as control resumes and suspends in a coroutine.

A coroutine does not start from the beginning on each activation; it is activated at the point of last suspension.

  • All coroutines inherit from base type uBaseCoroutine:

  • Two different approaches are possible for activating another coroutine:

    1. A semi-coroutine acts asymmetrically, like non-recursive routines, by implicitly reactivating the coroutine that previously activated it.
    2. A full coroutine acts symmetrically, like recursive routines, by explicitly activating a member of another coroutine, which directly or indirectly reactivates the original coroutine (activation cycle).

IMPORTANT

It is important to understand that calling a coroutine’s member by another coroutine does not cause a switch to the other coroutine. A switch only occurs when a resume is executed in the other coroutine’s member.

  • resume/suspend cause a context switch between coroutine stacks

  • first resume starts main on new stack (cocall); subsequent resumes reactivate last suspend.

  • suspend reactivates last resume

  • object becomes a coroutine on first resume; coroutine becomes an object when main ends

  • routine frame at the top of the stack knows where to activate execution

  • suspend/resume are protected members to prevent external calls. Why?

    • This restriction enforces encapsulation and ensures that only the coroutine itself or related classes can control its suspension and resumption, protecting its internal state and preventing misuse by external code.
    • By limiting access, the design maintains better control over the coroutine’s execution flow, which is essential for predictable and safe coroutine behaviour.
  • Coroutine main does not have to return before a coroutine object is deleted.

  • When deleted, a coroutine’s stack is always unwound and any destructors executed. Why?

    • essential for resource management and safety
    • By unwinding the stack, we ensure that all resources (like memory allocations or file handles) are properly released, and objects are destroyed in the correct order.

Semi-Coroutine

  • suspend(): returns to the last resume
    • A call to suspend may appear in any member of the coroutine, but normally it is used only in the coroutine main or non-public members called directly or indirectly from the coroutine main.
    • At each suspend, the complete state of the suspender is retained on the stack of the coroutine.
  • resume(): transfers to the last suspend
    • A call to resume may appear in any member of the coroutine, but normally it is used only in the public members.
    • At each resume, the complete state of the resumer is retained on the stack of the cocaller, including the call to the coroutine’s member that executes the resume.

Full Coroutine

Less common form of coroutine.

Full coroutines are somewhat like recursive routines in that the coroutine calls itself directly or indirectly to form a cycle. However, unlike recursion, a new instance of the coroutine is not created for each call.

Key

The key point is that resume reactivates the current coroutine, i.e., this, wherever it is suspended.