Simple example The example below shows the basic idea of setjmp. There, main() calls first(), which in turn calls second(). Then, second() jumps back into main(), skipping first()'s call of printf(). • include • include static jmp_buf buf; void second() { printf("second\n"); // prints longjmp(buf, 1); // jumps back to where setjmp was called - making setjmp now return 1 } void first() { second(); printf("first\n"); // does not print } int main() { if (!setjmp(buf)) { first(); // when executed, setjmp returned 0 } else { // when longjmp jumps back, setjmp returns 1 printf("main\n"); // prints } return 0; } When executed, the above program will output: second main Notice that although the first() subroutine gets called, "first" is never printed, as second() never returns control to first(). Instead, "main" is printed when the conditional statement if (!setjmp(buf)) is checked a second time.
Exception handling In this example, setjmp is used to bracket exception handling, like
try in some other languages. The call to longjmp is analogous to a throw statement, allowing an exception to return an error status directly to the setjmp. The following code adheres to the
1999 ISO C standard and
Single UNIX Specification by invoking setjmp in a limited range of contexts: • As the condition to an if, switch or iteration statement • As above in conjunction with a single ! or comparison with an integer constant • As a statement (with the return value unused) Following these rules can make it easier for the implementation to create the environment buffer, which can be a sensitive operation. More general use of setjmp can cause undefined behaviour, such as corruption of local variables; conforming compilers and environments are not required to protect or even warn against such usage. However, slightly more sophisticated idioms such as {{code|1=switch ((exception_type = setjmp(env))) { }|2=c|style=white-space:nowrap;}} are common in literature and practice, and remain relatively portable. A simple conforming methodology is presented below, where an additional variable is maintained along with the state buffer. This variable could be elaborated into a structure incorporating the buffer itself. In a more modern-looking example, the usual "try" block would be implemented as a setjmp (with some preparation code for multilevel jumps, as seen in ), the "throw" as longjmp with the optional parameter as the exception, and the "catch" as the "else" block under "try". • include • include • include • include static void first(); static void second(); /* Use a file scoped static variable for the exception stack so we can access * it anywhere within this translation unit. */ static jmp_buf exception_env; static int exception_type; int main(void) { volatile char* mem_buffer = NULL; if (setjmp(exception_env)) { // if we get here there was an exception printf("first failed, exception type: %d\n", exception_type); } else { // Run code that may signal failure via longjmp. puts("calling first"); first(); mem_buffer = (char*)malloc(300); // allocate a resource printf("%s\n", strcpy(mem_buffer, "first succeeded")); // not reached } free(mem_buffer); // NULL can be passed to free, no operation is performed return 0; } static void first() { jmp_buf my_env; puts("entering first"); // reached memcpy(my_env, exception_env, sizeof my_env); // store value of exception_env in my_env since exception_env will be reused switch (setjmp(exception_env)) { case 3: // if we get here there was an exception. printf("second failed, exception type: 3; remapping to type 1"); exception_type = 1; default: // fall through memcpy(exception_env, my_env, sizeof exception_env); // restore exception stack longjmp(exception_env, exception_type); // continue handling the exception case 0: // normal, desired operation printf("calling second"); // reached second(); printf("second succeeded"); // not reached } memcpy(exception_env, my_env, sizeof exception_env); // restore exception stack puts("leaving first"); // never reached } static void second() { printf("entering second" ); // reached exception_type = 3; longjmp(exception_env, exception_type); // declare that the program has failed printf("leaving second"); // not reached } This program's output is: calling first entering first calling second entering second second failed, exception type: 3; remapping to type 1 first failed, exception type: 1
Cooperative multitasking C99 provides that longjmp is guaranteed to work only when the destination is a calling function, i.e., that the destination scope is guaranteed to be intact. Jumping to a function that has already terminated by return or longjmp is undefined. However, most implementations of longjmp do not specifically destroy local variables when performing the jump. Since the context survives until its local variables are erased, it could actually be restored by setjmp. In many environments (such as Really Simple Threads and TinyTimbers), idioms such as can allow a called function to effectively pause-and-resume at a setjmp. This is exploited by thread libraries to provide
cooperative multitasking facilities without using
setcontext or other
fiber facilities. Considering that setjmp to a child function will generally work unless sabotaged, and setcontext, as part of POSIX, is not required to be provided by C implementations, this mechanism may be portable where the setcontext alternative fails. Since no exception will be generated upon overflow of one of the multiple stacks in such a mechanism, it is essential to overestimate the space required for each context, including the one containing main() and including space for any signal handlers that might interrupt regular execution. Exceeding the allocated space will corrupt the other contexts, usually with the outermost functions first. Unfortunately, systems requiring this kind of programming strategy are often also small ones with limited resources. • include • include jmp_buf mainTask, childTask; void call_with_cushion(); void child(); int main() { if (!setjmp(mainTask)) { call_with_cushion(); // child never returns, yield } // execution resumes after this "}" after first time that child yields while (true) { printf("Parent\n"); if (!setjmp(mainTask)) { longjmp(childTask, 1); // yield - note that this is undefined under C99 } } } void call_with_cushion() { char space[1000]; // Reserve enough space for main to run space[999] = 1; // Do not optimize array out of existence child(); } void child() { while (true) { printf("Child loop begin\n"); if (!setjmp(childTask)) { longjmp(mainTask, 1); // yield - invalidates childTask in C99 } printf("Child loop end\n"); if (!setjmp(childTask)) { longjmp(mainTask, 1); // yield - invalidates childTask in C99 } } /* Don't return. Instead we should set a flag to indicate that main() should stop yielding to us and then longjmp(mainTask, 1) */ } == References ==