This question comes from Practical usage of setjmp and longjmp in C and How to implement coroutine within for loop in c which I asked.
jmp_buf bufferA, bufferB;
void routineB(); // forward declaration
void routineA()
{
int r = 0;
printf("(A1)\n");
if (setjmp(bufferA) == 0) {
r++;
alloca(2048);
routineB();
}
printf("(A2) r=%d\n",r);
if (setjmp(bufferA) == 0) {
r++;
longjmp(bufferB, 1);
}
printf("(A3) r=%d\n",r);
if (setjmp(bufferA) == 0) {
r++;
longjmp(bufferB, 1);
}
printf("(A4) r=%d\n",r);
}
void routineB()
{
int r = 0;
printf("(B1)\n");
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B2) r=%d\n", r);
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B3) r=%d\n", r);
if (setjmp(bufferB) == 0) {
r++;
longjmp(bufferA, 1);
}
printf("(B4) r=%d never reach\n", r);
}
int main()
{
printf("main\n");
routineA();
return 0;
}
I am studying about the coroutine implementation by C. and trying to see what happened in the stack after longjmp
.
Question 1:
What's the magic makes the stack of routineB
alive after using alloca(2048)
?
I heard alloca
is evil but why it makes the stack looks like expanded.
Should I use it like this?
Output:
main
(A1)
(B1)
(A2) r=1
(B2) r=1
(A3) r=2
(B3) r=2
(A4) r=3
Question 2:
After removing alloca(2048)
. it gives different result after tell complier disable optimization(-O2).
-O0
main
(A1)
(B1)
(A2) r=1
(B2) r=6356584
(A3) r=2
(B3) r=6356584
(A4) r=3
-O2
main
(A1)
(B1)
(A2) r=1
(B2) r=0
(A3) r=1
(B3) r=0
(A4) r=1
if it's not undefined, how to make the code get the same behaviour? if it's, Please forget Q2.
Here's an article about implementing coros with setjmp/longjmp/alloca: https://fanf.livejournal.com/105413.html .
The idea is that in order for B to preserve it's full context (not just registers (preserved by setjmp) but also local, on-stack variables) when long-jumping back to A, B needs its own stack or at least it needs to make sure whatever A does won't overwrite B's variables.
alloca
is a way to achieve that without delving into assembly.
alloca
will basically move B much further on the stack than A is so that unless A uses deep recursion or anything that'd make it use more than 2KiB (in this case) of its stack, A and B will keep their on-stack local variables separate.
(This technique is quite naturally not strictly conforming C, and it'd be even less so if you used back-and-forth jumps between multiple malloc
'd stacks.)