MAKECONTEXT(3)

ИМЯ
makecontext, swapcontext - управляет пользовательским контекстом

ОБЗОР
#include <ucontext.h>

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);

ОПИСАНИЕ
В окружении, подобном SysV, имеется тип данных mcontext_t, определённый в файле <ucontext.h>, и четыре функции — getcontext(3), setcontext(3), makecontext() и swapcontext(), которые позволяют контексту пользовательского уровня переключаться между несколькими нитями внутри одного процесса.

Описание типа и первых двух функций смотрите в getcontext(3).

Функция makecontext() изменяет контекст, на который указывает ucp (полученный из вызова getcontext(3)). Перед вызовом makecontext(), вызывающий должен выделить новый стек для этого контекста и присвоить его адрес ucp->uc_stack, и определить последующий контекст и присвоить его адрес ucp->uc_link. Позднее, когда этот контекст активируется (с помощью setcontext(3) или swapcontext()), вызывается функция func и ей передаётся набор целочисленных аргументов (int), указанных после argc; вызывающий должен указать количество этих аргументов в argc. После возврата из функции активируется последующий контекст. Если указатель последующего контекста равен NULL, то нить завершается.

Функция swapcontext() сохраняет текущий контекст в структуру, на которую указывает oucp, и после этого активирует контекст, на который указывает ucp.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении swapcontext() не возвращает выполнение (но мы можем вернуться позднее при активации oucp и это будет выглядеть как если бы swapcontext() вернула 0). При ошибке swapcontext() возвращает -1 и изменяет errno соответствующим образом.

ОШИБКИ

ENOMEM
    Осталось недостаточно стекового пространства.

ВЕРСИИ
Функции makecontext() и swapcontext() появились в glibc начиная с версии 2.1.

АТРИБУТЫ
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение
makecontext() | Безвредность в нитях | MT-Safe race:ucp
swapcontext() | Безвредность в нитях | MT-Safe race:oucp race:ucp

СООТВЕТСТВИЕ СТАНДАРТАМ
SUSv2, POSIX.1-2001. В POSIX.1-2008 удалены определения makecontext() и swapcontext() со ссылкой на проблемы с переносимостью и рекомендацией переписать приложение с использование нитей POSIX.

ЗАМЕЧАНИЯ
Назначение ucp->uc_stack подобно описанному в sigaltstack(2), а именно: данная структура содержит начало и размер области памяти, которая будет использоваться как стек, независимо от направления роста стека. То есть, в пользовательской программе нет необходимости учитывать это направление. В архитектурах, где тип int и указатель имеют одинаковый размер (например, x86-32, оба типа имеют размер 32 бита), вы можете передавать указатели в аргументах makecontext() после argc. Однако, это не гарантирует переносимость, не определено в стандарте и не работает на архитектурах, где указатели больше int. Тем не менее, начиная с версии 2.8, в glibc внесены изменения в makecontext(), которые позволяют это и на некоторых 64-битных архитектурах (например, x86-64).

ПРИМЕР

В программе, показанной далее, демонстрируется использование getcontext(3), makecontext() и swapcontext(). Вот результат запуска этой программы:
$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: запущена
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: запущена
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: возврат
func1: возврат
main: выход

  Исходный код программы
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
static ucontext_t uctx_main, uctx_func1, uctx_func2;
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void
func1(void)
{
    printf("func1: запущена\n");
    printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
    if (swapcontext(&uctx_func1, &uctx_func2) == -1)
        handle_error("swapcontext");
    printf("func1: возврат\n");
}
static void
func2(void)
{
    printf("func2: запущена\n");
    printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
    if (swapcontext(&uctx_func2, &uctx_func1) == -1)
        handle_error("swapcontext");
    printf("func2: возврат\n");
}
int
main(int argc, char *argv[])
{
    char func1_stack[16384];
    char func2_stack[16384];
    if (getcontext(&uctx_func1) == -1)
        handle_error("getcontext");
    uctx_func1.uc_stack.ss_sp = func1_stack;
    uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
    uctx_func1.uc_link = &uctx_main;
    makecontext(&uctx_func1, func1, 0);
    if (getcontext(&uctx_func2) == -1)
        handle_error("getcontext");
    uctx_func2.uc_stack.ss_sp = func2_stack;
    uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
    /* последующий контекст — f1(), если argc > 1 */
    uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
    makecontext(&uctx_func2, func2, 0);
    printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
    if (swapcontext(&uctx_main, &uctx_func2) == -1)
        handle_error("swapcontext");
    printf("main: выход\n");
    exit(EXIT_SUCCESS);
}

СМОТРИТЕ ТАКЖЕ
sigaction(2), sigaltstack(2), sigprocmask(2), getcontext(3), sigsetjmp(3)