How to Implement Coroutines in C++

How do you implement Coroutines in C++

Yes it can be done without a problem. All you need is a little assembly code to move the call stack to a newly allocated stack on the heap.

I would look at the boost::coroutine library.

The one thing that you should watch out for is a stack overflow. On most operating systems overflowing the stack will cause a segfault because virtual memory page is not mapped. However if you allocate the stack on the heap you don't get any guarantee.
Just keep that in mind.

How to implement coroutine within for loop in c

What you are trying to achieve using your code here is not defined.

Quoting C11, chapter §7.13.2.1p2

The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If there has been no such invocation, or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.

Emphasis mine

Regarding what counts as terminated execution:

Quoting C11, chapter §note248

For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.

So, say you call a() first, and it calls b() after setting the a_buf. Now b() sets the b_buf and jumps back to a. At this point b's execution has terminated and if you jump back to b_buf, the behavior is undefined.

One possible solution for your problem could be to define functions a_step() and b_step() which perform just a single step of a() and b() respectively. Then call them alternatively in a loop.

Different ways of implementing a coroutine

Your option (1) is indeed a stackless coroutine, and this is how it's implemented in Kotlin, and usually Javascript (async/await), for example. This is how you do it when you don't necessarily have the low-level control of the call stack that other methods require. Languages that use this strategy require suspendable functions to be marked in some way. That's called the "red/blue function problem". See: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/

Languages that do have low-level control of the call stack can implement coroutines in various ways, but none of them look like your option (2). They generally involve copying data into and out of the call stack when required (like Java project Loom) or using a completely different data structure in place of the call stack (like early Go implementations). In these languages, coroutine-callable functions don't usually need special markings.

C++ coroutines: implementing taskvoid

Apparently, the usual suspect was the criminal: specialization! From the standard itself [temp.expl.spec]/7

When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

To avoid issue, let's make it as simple as possible: task_promise can be a non template and the member specialization is declared as soon as possible:

template<class T=void>
struct task{
//...
struct task_promise{
//...
};
};

//member specialization declared before instantiation of task<void>;
template<>
struct task<void>::task_promise{
//...
};

How to create lua coroutines using the lua c api?

The following is C code yields the string "Wonderfull" 4 times. And returns the string "End" before terminating the coroutine.

static int kfunction(lua_State* L, int status, lua_KContext ctx)
{
static int x = 0;

if (x < 3)
{
x++;
lua_pushfstring(L, "Wonderfull");
return lua_yieldk(L, 1, 0, kfunction);
}
lua_pushfstring(L, "End");
return 1;
}

static int iter(lua_State* L)
{
lua_pushfstring(L, "Wonderfull");
return lua_yieldk(L, 1, 0, kfunction);
}

int luaopen_module(lua_State* L) {
// initial function which is called when require("module") is run

lua_State* n = lua_newthread(L);
lua_setglobal(L, "coroutine_function");

lua_pushcfunction(n, iter);

return 0;
}

Using the C module in Lua:

require("module")

print(coroutine.resume(coroutine_function)) -- true Wonderfull
print(coroutine.resume(coroutine_function)) -- true Wonderfull
print(coroutine.resume(coroutine_function)) -- true Wonderfull
print(coroutine.resume(coroutine_function)) -- true Wonderfull
print(coroutine.resume(coroutine_function)) -- true End
print(coroutine.resume(coroutine_function)) -- false cannot resume dead coroutine

int iter(lua_State* L) is called when coroutine.resume is called for the first time. The subsequent calls are to int kfunction(lua_State* L, int status, lua_KContext ctx).

4th argument to lua_yieldk can be thorugh as the next function Lua should call to get the next yield or return value.

Documentation: Handling Yields in C



Related Topics



Leave a reply



Submit