1

Here is part code:

void a()
{
    printf("entering a\n");
    int i;
    for(i = 0; i < 3; i++){
        if(setjmp(a_buf) == 0) {
            printf("A step %d\n", i);
            b();
        } else {
            longjmp(b_buf, 1);
        }
    }
    printf("returning from a\n");
}

void b()
{
    printf("entering b\n");
    int i;
    for(i = 0; i < 5; i++){
        if(setjmp(b_buf) == 0) {
            printf("B step %d\n", i);
            a();
        } else {
            longjmp(a_buf, 1);
        }
    }
    printf("returning from b\n");
}

I have two processes a & b. How to make them works as coroutine. Wish them doing A Step 0 then B Step 0 then back to A Step 1... until both finished. But looks like counter i never changed.

JustWe
  • 4,250
  • 3
  • 39
  • 90
  • If you want to serialize execution of 2 processes, why do you use 2 processes at all? – Gerhardh May 23 '18 at 08:22
  • @Gerhardh I having the same problem. https://stackoverflow.com/questions/50383520/why-should-i-use-coroutine-in-c-c – JustWe May 23 '18 at 08:25
  • That's the same weird `setjmp`-massacre. Then put it this way: If you want serial execution of single steps why would you want to organize them in such a set of functions? Just call the stepts within one function. Or maybe maintain a static variable in the second function that holds information about which step to execute next. Or are you searching for a problem that might match your semi-solution? – Gerhardh May 23 '18 at 09:06
  • @Gerhardh I am studying about how to make the _correct_ coroutine implementation in C. Then wiki and google told me the solution need using `setjmp`. So I am searching the right way lead me to the real 'coroutine'. don't care it's good or not, just wondering how to make it works. – JustWe May 23 '18 at 09:18
  • Which problem do you want to solve? Using coroutines just for the sake of using coroutines sounds a bit strange. The comment to your linked question is very true. – Gerhardh May 23 '18 at 09:23
  • Also your other question is based on a (incorrect) answer to a question looking for some usecase for `setjmp`. It doesn't make much sense to insist on using it unless it really solves your problem. Which you don't tell us. – Gerhardh May 23 '18 at 09:27
  • @Gerhardh That incorrect answer is the beginning of all of these question I asked...I am just trying to know what's coroutine in C. – JustWe May 23 '18 at 09:30
  • I'm having the exactly same question as you. But people created this evil `setjmp` and tell me it's way of the coroutine. – JustWe May 23 '18 at 09:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171611/discussion-between-gerhardh-and-jiu). – Gerhardh May 23 '18 at 09:55

4 Answers4

4

The setjmp() and longjmp() functions can only be used to exit from a nested subroutine, as a sort of "throw/catch". They cannot be used to reenter a subroutine that has already exited, either through a return or through a longjmp(). In short, they cannot be used this way.

The only well-defined way to implement coroutines in C is as a state machine. There is no way to implement coroutines as normal subroutines, as this is incompatible with the C stack.

3

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.

Community
  • 1
  • 1
Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • I understand _At this point b's execution has terminated_. So in https://stackoverflow.com/questions/14685406/practical-usage-of-setjmp-and-longjmp-in-c the example of the first answer is wrong? – JustWe May 23 '18 at 08:18
  • 1
    @Jiu yes the example is completely wrong. Read the comments for that answer. It is possible for very primitive functions and not supported by the standard. It just works because of some artifacts of how *some* compilers work. – Ajay Brahmakshatriya May 23 '18 at 08:21
0

Please look at the "s_task" coroutine, which may be the answer for you --

https://github.com/xhawk18/s_task

features --

  1. written in pure c and asm, without c++ required
  2. asm code from boost context, which is robust and effective
  3. add keywords await and async
  4. supports various platforms, such as windows, linux (arm,x86,mips), stm32, stm8
  5. have integrated with libuv for network programming
  6. "event" and "mutex" object for communication between coroutings
xhawk18
  • 169
  • 1
  • 4
0

Can be solved elegantly with coroutines from the STC library, using Duff's device.

Godbolt: https://godbolt.org/z/354fhKKMx

#include <stc/coroutine.h>
#include <stdio.h>

struct a { int i; int cco_state; };
struct b { int i; int cco_state; };

int coro_a(struct a* g)
{
    cco_routine (g) {
        printf("entering a\n");
        for (g->i = 0; g->i < 3; g->i++) {
            printf("A step %d\n", g->i);
            cco_yield();
        }
        cco_final:
        printf("returning from a\n");
    }
    return 0; // done
}

int coro_b(struct b* g)
{
    cco_routine (g) {
        printf("entering b\n");
        for (g->i = 0; g->i < 5; g->i++) {
            printf("B step %d\n", g->i);
            cco_yield();
        }
        cco_final:
        printf("returning from b\n");
    }
    return 0;
}

int main(void)
{
    struct a a = {0};
    struct b b = {0};
    
    while (coro_a(&a) | coro_b(&b)) {}
}
tylo
  • 41
  • 3