Setcontext

From Wikipedia, the free encyclopedia

The correct title of this article is setcontext. The initial letter is shown capitalized due to technical restrictions.

setcontext is one of a family of C library functions (the others being getcontext, makecontext and swapcontext) used for context control. The setcontext family allows the implementation in C of advanced control flow patterns such as iterators, fibers and coroutines. They may be viewed as an advanced version of setjmp/longjmp; where the latter just allows a single non-local jump up the stack, setcontext allows the creation of multiple cooperative threads of control, each with its own stack.

[edit] Specification

Setcontext is specified in POSIX.1-2001 and SUSv2.

The functions and associated types are defined in the ucontext.h system header file. This includes the ucontext_t type, with which all four functions operate:

typedef struct ucontext {
        struct ucontext *uc_link;
        sigset_t         uc_sigmask;
        stack_t          uc_stack;
        mcontext_t       uc_mcontext;
        ...
} ucontext_t;

uc_link points to the context which will be resumed when the current context exits, if the context was created with makecontext (a secondary context). uc_sigmask is used to store the set of signals blocked in the context, and uc_stack is the stack used by the context. uc_mcontext stores execution state, including all registers and CPU flags, the instruction pointer, and the stack pointer; mcontext_t is an opaque type.

The functions are:

  • int setcontext(const ucontext_t *ucp)
This function transfers control to the context in ucp. Execution continues from the point at which the context was stored in ucp. setcontext does not return.
  • int getcontext(ucontext_t *ucp)
Saves current context into ucp. This function returns in two possible states: after the initial call, and when the context in ucp is switched to via setcontext or swapcontext. The getcontext function does not provide a return 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 declared as volatile to avoid constant propagation or other compiler optimisations.
  • void makecontext(ucontext_t *ucp, void *func(), int argc, ...)
The makecontext function sets up an alternate thread of control in ucp, which has previously been initialised using getcontext. The ucp.uc_stack member should be pointed to an appropriately sized stack; the SIGSTKSZ constant is commonly used. When ucp is jumped to using setcontext or swapcontext, execution will begin at the entry point to the function pointed to by func, with argc arguments as specified. When func terminates, control is returned to ucp.uc_link.
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
Transfers control to ucp and saves the current execution state into oucp.

The setcontext family of functions are described in the Single Unix Specification, version 2. Not all Unix-like operating systems currently provide them.

[edit] Example

The example below demonstrates a simple iterator using setcontext. This form of example is unlikely to be widely seen: as setcontext is somewhat cumbersome to use effectively, programmers writing cooperatively multitasked applications often choose to use a wrapper library such as GNU Portable Threads. Most code using setcontext appears in such wrapper libraries, in high-level programming language implementations, or in emulators.

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

/* This is the iterator function. It is entered on the first call to 
 * swapcontext, and loops from 0 to 10. Each value is saved in 
 * yield_i, 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 contextMain1 (see 
 * below).
 */

static void 
loop(ucontext_t *contextLoop, ucontext_t *contextOther, int *yield_i)
{
   int i;
   for (i = 0; i < 10; ++i)
   {
       /* Write the loop counter into the iterator return location. */
       *yield_i = i;
       
       /* Save the loop context (where we are now) into contextLoop, 
        * and switch to contextOther.
        */
       swapcontext(contextLoop, contextOther);
   }
   
   /* The function falls through to the calling context with an implicit
    * setcontext(&contextLoop->uc_link);
    */
} 
 
int 
main(void)
{
   /* The three contexts:
    *    (1)contextMain1 : The point in main() to which loop() will return.
    *    (2)contextMain2 : The point in main() to which control from loop()
    *                             will flow by switching contexts.
    *    (3)contextLoop  : The point in loop() to which control from main()
    *                             will flow by switching contexts.
    */
   ucontext_t contextMain1, contextMain2, contextLoop;

   /* The stack for the iterator function. */
   char stackIterator[SIGSTKSZ];

   /* Flag indicating that the iterator has completed. */
   volatile int iteratorNotFinished;

   /* The iterator return value. */
   volatile int yield_i;
 
   /* Initialise the iterator context. uc_link points to contextMain1, 
    * the point to return to when the iterator finishes.
    */
   contextLoop.uc_link          = &contextMain1;
   contextLoop.uc_stack.ss_sp   = stackIterator;
   contextLoop.uc_stack.ss_size = sizeof(stackIterator);

   /* Further initialize the context: maybe unnecessary*/
   getcontext(&contextLoop);

   /* Fill in contextLoop so that it informs switchcontext
    * to start loop().
    */
   makecontext(&contextLoop, (void (*) (void)) loop, 3,
         &contextLoop, &contextMain2, &yield_i);

   /* Set the incompletion flag. */      
   iteratorNotFinished = 1;

   /* Save the current context into contextMain1.
    * When loop() is finished, control flow
    * will return to this point.
    */
   getcontext (&contextMain1);
   
   if (iteratorNotFinished)
   {
       /* Set iteratorNotFinished to 0 so that when the previous 
        * getcontext is returned to via uc_link, the above
        * if is false and the iterator is not restarted.
        */
       iteratorNotFinished = 0;
       
       while (1)
       {
           /* Save this point into contextMain2 and switch into the
            * iterator; the first call will begin loop, 
            * subsequent calls will switch to the swapcontext 
            * in the function.
            */
           swapcontext (&contextMain2, &contextLoop);
           printf ("%d\n", yield_i);
       }
    }

   return 0;
}

[edit] External links