Setcontext

Last updated

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; whereas the latter allows only a single non-local jump up the stack, setcontext allows the creation of multiple cooperative threads of control, each with its own stack.

Contents

Specification

setcontext was specified in POSIX.1-2001 and the Single Unix Specification, version 2, but not all Unix-like operating systems provide them. POSIX.1-2004 obsoleted these functions [1] , and in POSIX.1-2008 they were removed, with POSIX Threads indicated as a possible replacement.

Definitions

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:

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 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:

Example

The example below demonstrates an iterator using setcontext.

#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 function makecontext requires additional parameters to be type int, but the example passes pointers. Thus, the example may fail on 64-bit machines (specifically LP64-architectures, where sizeof(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.

Related Research Articles

C is a general-purpose computer programming language. It was created in the 1970s by Dennis Ritchie, and remains very widely used and influential. By design, C's features cleanly reflect the capabilities of the targeted CPUs. It has found lasting use in operating systems, device drivers, and protocol stacks, but its use in application software has been decreasing. C is commonly used on computer architectures that range from the largest supercomputers to the smallest microcontrollers and embedded systems.

Berkeley sockets is an application programming interface (API) for Internet sockets and Unix domain sockets, used for inter-process communication (IPC). It is commonly implemented as a library of linkable modules. It originated with the 4.2BSD Unix operating system, which was released in 1983.

Coroutines are computer program components that allow execution to be suspended and resumed, generalizing subroutines for cooperative multitasking. Coroutines are well-suited for implementing familiar program components such as cooperative tasks, exceptions, event loops, iterators, infinite lists and pipes.

In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following:

<span class="mw-page-title-main">C syntax</span> Set of rules defining correctly structured programs

The syntax of the C programming language is the set of rules governing writing of software in C. It is designed to allow for programs that are extremely terse, have a close relationship with the resulting object code, and yet provide relatively high-level data abstraction. C was the first widely successful high-level language for portable operating-system development.

<span class="mw-page-title-main">Pointer (computer programming)</span> Object which stores memory addresses in a computer program

In computer science, a pointer is an object in many programming languages that stores a memory address. This can be that of another value located in computer memory, or in some cases, that of memory-mapped computer hardware. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer. As an analogy, a page number in a book's index could be considered a pointer to the corresponding page; dereferencing such a pointer would be done by flipping to the page with the given page number and reading the text found on that page. The actual format and content of a pointer variable is dependent on the underlying computer architecture.

In computer programming, a callback or callback function is any reference to executable code that is passed as an argument to another piece of code; that code is expected to call back (execute) the callback function as part of its job. This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback. They are also called blocking and non-blocking.

In computer programming, an entry point is the place in a program where the execution of a program begins, and where the program has access to command line arguments.

In computing, sigaction is a function API defined by POSIX to give the programmer access to what should be a program's behavior when receiving specific OS signals.

typedef is a reserved keyword in the programming languages C, C++, and Objective-C. It is used to create an additional name (alias) for another data type, but does not create a new type, except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type. As such, it is often used to simplify the syntax of declaring complex data structures consisting of struct and union types, although it is also commonly used to provide specific descriptive type names for integer data types of varying sizes.

In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook.

<span class="mw-page-title-main">Recursion (computer science)</span> Use of functions that call themselves

In computer science, recursion is a method of solving a computational problem where the solution depends on solutions to smaller instances of the same problem. Recursion solves such recursive problems by using functions that call themselves from within their own code. The approach can be applied to many types of problems, and recursion is one of the central ideas of computer science.

The power of recursion evidently lies in the possibility of defining an infinite set of objects by a finite statement. In the same manner, an infinite number of computations can be described by a finite recursive program, even if this program contains no explicit repetitions.

sizeof is a unary operator in the programming languages C and C++. It generates the storage size of an expression or a data type, measured in the number of char-sized units. Consequently, the construct sizeof (char) is guaranteed to be 1. The actual number of bits of type char is specified by the preprocessor macro CHAR_BIT, defined in the standard include file limits.h. On most modern computing platforms this is eight bits. The result of sizeof has an unsigned integer type that is usually denoted by size_t.

The C and C++ programming languages are closely related but have many significant differences. C++ began as a fork of an early, pre-standardized C, and was designed to be mostly source-and-link compatible with C compilers of the time. Due to this, development tools for the two languages are often integrated into a single product, with the programmer able to specify C or C++ as their source language.

setjmp.h is a header defined in the C standard library to provide "non-local jumps": control flow that deviates from the usual subroutine call and return sequence. The complementary functions setjmp and longjmp provide this functionality.

stdarg.h is a header in the C standard library of the C programming language that allows functions to accept an indefinite number of arguments. It provides facilities for stepping through a list of function arguments of unknown number and type. C++ provides this functionality in the header cstdarg.

In computing, the producer-consumer problem is a family of problems described by Edsger W. Dijkstra since 1965.

select is a system call and application programming interface (API) in Unix-like and POSIX-compliant operating systems for examining the status of file descriptors of open input/output channels. The select system call is similar to the poll facility introduced in UNIX System V and later operating systems. However, with the c10k problem, both select and poll have been superseded by the likes of kqueue, epoll, /dev/poll and I/O completion ports.

Getopt is a C library function used to parse command-line options of the Unix/POSIX style. It is a part of the POSIX specification, and is universal to Unix-like systems. It is also the name of a Unix program for parsing command line arguments in shell scripts.

In computer programming, the async/await pattern is a syntactic feature of many programming languages that allows an asynchronous, non-blocking function to be structured in a way similar to an ordinary synchronous function. It is semantically related to the concept of a coroutine and is often implemented using similar techniques, and is primarily intended to provide opportunities for the program to execute other code while waiting for a long-running, asynchronous task to complete, usually represented by promises or similar data structures. The feature is found in C#, C++, Python, F#, Hack, Julia, Dart, Kotlin, Rust, Nim, JavaScript, Swift and Zig.

References

  1. 1 2 The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition