Skip to content

How does coroutine execution work?

Juju Adams edited this page Oct 31, 2021 · 25 revisions

This document explains the process by which coroutines are built using function calls and how coroutines are executed. If you're looking for an explanation of the inner working of the GML syntax extension, please see How do we extend GML?

Coroutine execution in this library centres around the "coroutine root struct", a data container that both manages the execution of the coroutine as well as stores coroutine state (the variables that you read and write whilst the coroutine is executing). When a coroutine is created, it is registered in a global list of coroutines that are processed every frame. When a coroutine completes it is removed from that global list meaning the memory allocated to it can be freed as well, provided that your code isn't holding a reference to it.

When a coroutine is defined, the root struct is given a sequence of instructions it must carry out. Instructions can simply be "run this block of GML", or the can be flow control (loops and branching), or they can be behaviours that require the root struct to wait for further input. Instructions are added to the root struct using function calls that look like this:

__CoroutineFunction(function()
{
    i = 0;
});

__CoroutineWhile(function()
{
    return (i < 6);
});

__CoroutineFunction(function()
{
    show_debug_message("Six messages!");
    show_debug_message("(i=" + string(i) + ")");
}

__CoroutineEndLoop();

We call a sequence of function calls that adds instructions to a coroutine a "generator function". Note how the coroutine generator function requires a step to start the while-loop __CoroutineWhile() and another instruction to end the loop __CoroutineEndLoop(). This generator function is equivalent to the following standard GML:

var i = 0;
while (i < 6)
{
    show_debug_message("Six messages!");
    show_debug_message("(i=" + string(i) + ")");
}

It's obvious by comparing the generator function and its standard GML equivalent that the standard GML is much more compact! We solve this problem by extending what syntax can be used in GML - please see How do we extend GML? for more information.