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)