Hello!
Recently I was looking for a way to move all my logic implementation for a plugin/instrument in a single-separated area of my project.
For this purpose many VST instruments feature a scripting language. The scripting language can be used to build a GUI, to handle the MIDI part of the project and to control the “internals” of the instrument in a nice way.
Many scripting languages often contain an instruction called “wait(time)”.
Now it’s clear that I can’t “wait” in the processBlock callback, unless I want to make the VST to die horribly. What the wait function actually does is to skip executing the rest of the code of the function until the wait is over and then resume the function from where it was interrupted.
A non scripted solution could be to use a different thread (like the message thread), as many applications do, but it would mean that I would be limited somehow in the number of concurrent scripts running (since thread switching have a cost), I would have race concurrency problems and any kind of headache that the threads have. Another option would be to have a kind of scheduler (a very common solution) which I could use to post functions to be executed later in time.
But maybe the code I want to execute can’t be easily wrapped inside a function. Consider this example:
for (int i; i< 100; ++i) {
wait(100);
if (x==true){
//do stuff
wait(1000);
} else
{
//do other stuff
wait(200);
}
//do other stuff
}
(how can I indent with the forum tools?)
Unless wrapping each slice of code into its own function and post them all together in the scheduler, it’s a very complex case scenario to handle, for a such simple code.
Is implementing a scripting language the only way to handle this behaviour? Maybe not.
Welcome Coroutines!
Coroutines are a very common thing in scripted languages like Lua. They are also called “green threads” or “fibers”. They are basically functions that can be interrupted and resumed from the point they were interrupted. In any code that involves a constant loop (like game engines) they are very popular. The tricky part is that coroutines are NOT available in the current version of C++. The good news is that they are being implemented in C++20. But what about now? How can we implement such a delightful thing in the current version of C++? It turns out that the problem is widely known and addressed with a nice trick by “spoiling” the switch statement. A code like this is legit in C++ (and C):
int function(void) {
static int i, state = 0;
switch (state) {
case 0: /* start of function /
for (i = 0; i < 10; i++) {
state = 1; / so we will come back to “case 1” /
return i;
case 1:; / resume control straight after the return */
}
}
}
The switch statement acts like a kind of dynamic GOTO and it can jump to different scopes of a function. This is ugly, but useful, because we can move into our code exactly like a scripting engine does. You can wrap the switch statement in a macro and with some cleverness have your custom coroutines. These are stackless coroutines, since the scope of the function is destroyed each time the function exits, the static keyword prevents this, but this also means that concurrent coroutines will share the same variables. To prevent this you can pass the coroutine pointer to a custom object containing its unique data and use the static keyword for data shared by all the running coroutines. These coroutines can return an integer value that you can use to make the coroutine “wait” for a certain amount of time (or samples).
This is the article by Simon Taham on the subject:
https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
There are many implementations of coroutines in c++, some based on an assembly trick to jump around code. Some are very heavy, since they often save all the function stack in memory which is a big no in the processBlock method. This implementation of coroutines is actually very light.
I just wanted to share this little discovery