setcontext is one of a family ofClibraryfunctions (the others beinggetcontext,makecontext andswapcontext) used forcontext control. Thesetcontext family allows the implementation in C of advancedcontrol flowpatterns such asiterators,fibers, andcoroutines. They may be viewed as an advanced version ofsetjmp/longjmp; whereas the latter allows only a single non-local jump up thestack,setcontext allows the creation of multiplecooperativethreads of control, each with its own stack.
setcontext is specified inPOSIX.1-2001 and theSingle Unix Specification, version 2, but not allUnix-likeoperating systems provide them.POSIX.1-2004 obsoleted these functions,[1] and inPOSIX.1-2008 they are not included, withPOSIX Threads indicated as a possible replacement.
The functions and associated types are defined in theucontext.h systemheader file. This includes theucontext_t type, with which all four functions operate:
typedefstruct{ucontext_t*uc_link;sigset_tuc_sigmask;stack_tuc_stack;mcontext_tuc_mcontext;...}ucontext_t;
uc_link points to the context which will be resumed when the current context exits, if the context was created withmakecontext (a secondary context).uc_sigmask is used to store the set ofsignals blocked in the context, anduc_stack is thestack used by the context.uc_mcontext storesexecutionstate, including allregisters andCPUflags, theinstruction pointer, and thestack pointer;mcontext_t is anopaque type.
The functions are:
intsetcontext(constucontext_t*ucp)ucp. Execution continues from the point at which the context was stored inucp.setcontext does not return.intgetcontext(ucontext_t*ucp)ucp. This function returns in two possible cases: after the initial call, or when a thread switches to the context inucp viasetcontext orswapcontext. Thegetcontext function does not provide areturn value to distinguish the cases (its return value is used solely to signal error), so the programmer must use an explicit flag variable, which must not be a register variable and must be declaredvolatile to avoidconstant propagation or othercompiler optimizations.voidmakecontext(ucontext_t*ucp,void(*func)(),intargc,...)makecontext function sets up an alternate thread of control inucp, which has previously been initialised usinggetcontext. Theucp.uc_stack member should be pointed to an appropriately sized stack; the constantSIGSTKSZ is commonly used. Whenucp is jumped to usingsetcontext orswapcontext, execution will begin at theentry point to the function pointed to byfunc, withargc arguments as specified. Whenfunc terminates, control is returned toucp.uc_link.intswapcontext(ucontext_t*oucp,ucontext_t*ucp)ucp and saves the current execution state intooucp.The example below demonstrates an iterator usingsetcontext.
#include<stdio.h>#include<stdlib.h>#include<ucontext.h>#include<signal.h>/* The three contexts: * (1) main_context1 : The point in main to which loop will return. * (2) main_context2 : The point in main to which control from loop will * flow by switching contexts. * (3) loop_context : The point in loop to which control from main will * flow by switching contexts. */ucontext_tmain_context1,main_context2,loop_context;/* The iterator return value. */volatileinti_from_iterator;/* This is the iterator function. It is entered on the first call to * swapcontext, and loops from 0 to 9. Each value is saved in i_from_iterator, * and then swapcontext used to return to the main loop. The main loop prints * the value and calls swapcontext to swap back into the function. When the end * of the loop is reached, the function exits, and execution switches to the * context pointed to by main_context1. */voidloop(ucontext_t*loop_context,ucontext_t*other_context,int*i_from_iterator){inti;for(i=0;i<10;++i){/* Write the loop counter into the iterator return location. */*i_from_iterator=i;/* Save the loop context (this point in the code) into ''loop_context'', * and switch to other_context. */swapcontext(loop_context,other_context);}/* The function falls through to the calling context with an implicit * ''setcontext(&loop_context->uc_link);'' */}intmain(void){/* The stack for the iterator function. */chariterator_stack[SIGSTKSZ];/* Flag indicating that the iterator has completed. */volatileintiterator_finished;getcontext(&loop_context);/* Initialise the iterator context. uc_link points to main_context1, the * point to return to when the iterator finishes. */loop_context.uc_link=&main_context1;loop_context.uc_stack.ss_sp=iterator_stack;loop_context.uc_stack.ss_size=sizeof(iterator_stack);/* Fill in loop_context so that it makes swapcontext start loop. The * (void (*)(void)) typecast is to avoid a compiler warning but it is * not relevant to the behaviour of the function. */makecontext(&loop_context,(void(*)(void))loop,3,&loop_context,&main_context2,&i_from_iterator);/* Clear the finished flag. */iterator_finished=0;/* Save the current context into main_context1. When loop is finished, * control flow will return to this point. */getcontext(&main_context1);if(!iterator_finished){/* Set iterator_finished so that when the previous getcontext is * returned to via uc_link, the above if condition is false and the * iterator is not restarted. */iterator_finished=1;while(1){/* Save this point into main_context2 and switch into the iterator. * The first call will begin loop. Subsequent calls will switch to * the swapcontext in loop. */swapcontext(&main_context2,&loop_context);printf("%d\n",i_from_iterator);}}return0;}
NOTE: this example is not correct,[1] but may work as intended in some cases. The functionmakecontext requires additional parameters to be typeint, but the example passes pointers. Thus, the example may fail on 64-bit machines (specificallyLP64-architectures, wheresizeof(void*)>sizeof(int)). This problem can be worked around by breaking up and reconstructing 64-bit values, but that introduces a performance penalty.
On architectures where int and pointer types are the same size (e.g., x86-32, where both types are 32 bits), you may be able to get away with passing pointers as arguments to makecontext() following argc. However, doing this is not guaranteed to be portable, is undefined according to the standards, and won't work on architectures where pointers are larger than ints. Nevertheless, starting with version 2.8, glibc makes some changes to
makecontext(3), to permit this on some 64-bit architectures (e.g., x86-64).
For get and set context, a smaller context can be handy:
#include<stdio.h>#include<ucontext.h>#include<unistd.h>intmain(intargc,constchar*argv[]){ucontext_tcontext;getcontext(&context);puts("Hello world");sleep(1);setcontext(&context);return0;}
This makes an infinite loop because context holds the program counter.
setcontext(3): get/set current user context – Linux Programmer'sManual – Library Functions from Manned.orgsetcontext(3) – FreeBSD Library FunctionsManual