I wanna use setjmp()
/longjmp()
to implement a coroutine system.
Then I decide to code a little .c file to test it. In MinGW, it's OK; I got the result I want.
But when I compile it in MSVC++, the program crashes: "access violation"
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
jmp_buf a;
int is_invoke=0;
void
action_1()
{
for ( ;; ) {
printf("hello~~~A\n");
if(!setjmp(a)) {
is_invoke=1;
return;
}
}
}
void
func()
{
if (is_invoke) {
longjmp(a,1);
}
action_1();
printf("end\n");
}
void
dummy()
{
;
}
int
main(int argc, char *argv[])
{
for ( ;; ) {
func();
dummy();
}
return 0;
}
The man page for setjmp says:
setjmp()
saves the stack context/environment inenv
for later use bylongjmp()
. The stack context will be invalidated if the function which calledsetjmp()
returns.
In a simple implementation you might suppose that a jmp_buf
contains an address to reset the stack pointer to and an address to jump to. As soon as you return from the function which saved the jmp_buf
, the stack frame pointed to by the jmp_buf
is no longer valid and may immediately become corrupted.
Or in other words, you can only rely on longjmp to act as a sort-of super-return
statement - never to go deeper.
I think the reason this works for you in mingw (and for me on Linux) is implementation-specific and possibly down to luck. There is another way - have you read Simon Tatham's evil coroutine macros essay?
Since you're invoking undefined behaviour, it's OK for one compiler to crash and another to appear to work. Both are correct - that's the beauty of undefined behaviour.
The trouble is that a saved context - the jmp_buf
- only remains valid as long as the function that called setjmp()
to set it has not returned.
The C99 standard (no longer the current standard, but this wording is unlikely to have changed significantly) says:
§7.13.2.1 The
longjmp
functionThe
longjmp
function restores the environment saved by the most recent invocation of thesetjmp
macro in the same invocation of the program with the correspondingjmp_buf
argument. If there has been no such invocation, or if the function containing the invocation of thesetjmp
macro has terminated execution208) in the interim, or if the invocation of thesetjmp
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.208) For example, by executing a
return
statement or because anotherlongjmp
call has caused a transfer to asetjmp
invocation in a function earlier in the set of nested calls.
Your code is exiting from action_1()
almost immediately, rendering the jmp_buf
saved by setjmp()
worthless.
I created this little demonstration of setjmp()
and longjmp()
a couple of years ago. It may help you.
/*
@(#)File: $RCSfile: setjmp.c,v $
@(#)Version: $Revision: 1.1 $
@(#)Last changed: $Date: 2009/10/01 16:41:04 $
@(#)Purpose: Demonstrate setjmp() and longjmp()
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 2009
*/
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
static jmp_buf target_location;
static void do_something(void)
{
static int counter = 0;
if (++counter % 10 == 0)
printf("---- doing something: %3d\n", counter);
if (counter % 1000 == 0)
{
printf("||-- doing_something: calling longjmp() with value -1\n");
longjmp(target_location, -1);
}
}
static void do_something_else(int i, int j)
{
printf("-->> do_something_else: (%d,%d)\n", i, j);
do_something();
if (i > 2 && j > 2 && j % i == 2)
{
printf("||-- do_something_else: calling longjmp() with value %d\n", (i + j) % 100);
longjmp(target_location, (i + j) % 100);
}
printf("<<-- do_something_else: (%d,%d)\n", i, j);
}
static void doing_stuff(void)
{
int i;
printf("-->> doing_stuff()\n");
for (i = rand() % 15; i < 30; i++)
{
int j;
do_something();
for (j = rand() % 10; j < 20; j++)
{
do_something_else(i, j);
}
}
printf("<<-- doing_stuff()\n");
}
static void manage_setjmp(void)
{
printf("-->> manage_setjmp()\n");
switch (setjmp(target_location))
{
case 0:
/* Initial return - get on with doing stuff */
doing_stuff();
break;
case -1:
/* Error return - terminate */
printf("<<-- manage_setjmp() - error return from setjmp()\n");
return;
default:
/* NB: not officially possible to assign the return from setjmp() */
printf("---- manage_setjmp() - non-error return from setjmp()\n");
doing_stuff();
break;
}
printf("<<-- manage_setjmp()\n");
}
int main(void)
{
printf("-->> main()\n");
manage_setjmp();
printf("<<-- main()\n");
return(0);
}
You cannot use setjmp/longjmp for coroutines. Use makecontext/swapcontext on POSIX, or fibers (CreateFiber, etc.) on windows.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With