Лучшие вопросы
Таймлайн
Чат
Перспективы
Setcontext
Из Википедии, свободной энциклопедии
Remove ads
setcontext — одна из библиотечных функций стандарта POSIX (в число других входят getcontext, makecontext и swapcontext), используемая для управления контекстом. Семейство setcontext позволяет реализовать на языке Си такие паттерны проектирования управления потоком, как итераторы, нити (fibers) и сопрограммы. Семейство можно рассматривать как расширенную версию setjmp/longjmp; в то время как последние позволяют только один нелокальный прыжок из стека, setcontext позволяет создание нескольких взаимодействующих потоков управления с собственными стеками.
Remove ads
Спецификация
Суммиров вкратце
Перспектива
setcontext определён в POSIX.1-2001 и во второй версии Single UNIX Specification, однако доступен не во всех UNIX-подобных операционных системах. Функции и связанные с ними типы определены в заголовочном файле ucontext.h. В их число входит тип ucontext_t, с которым взаимодействуют все четыре функции:
typedef struct ucontext {
struct ucontext *uc_link;
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
...
} ucontext_t;
uc_link указывает на контекст, который будет восстановлен при выходе из текущего контекста, если контекст создан с помощью makecontext (вторичный контекст). uc_sigmask используется для хранения сигналов, заблокированных в контексте, а uc_stack является стеком, используемым контекстом. uc_mcontext используется для хранения состояния исполнения, включая все регистры центрального процессора, счётчик команд и указатель стека; mcontext_t является непрозрачным (opaque) указателем.
Также определены следующие функции:
int setcontext(const ucontext_t *ucp)
- Эта функция переносит управление в контекст в
ucp. Исполнение продолжается с точки, на которой контекст был сохранён вucp. В случае успешного выполнения возврата изsetcontextне производится.
int getcontext(ucontext_t *ucp)
- Сохраняет текущий контекст в
ucp. Возврат из этой функции происходит в двух случаях: после первичного вызова или при переключении потока на контекст вucpс помощьюsetcontextилиswapcontext. Функцияgetcontextне предоставляет возвращаемого значения для разделения этих случаев (оно служит лишь для сообщения об ошибке), поэтому разработчик должен явным образом использовать переменную-флаг, объявленную без модификатора register и с модификатором volatile во избежание свёртывания константных выражений и других оптимизаций компилятора.
void makecontext(ucontext_t *ucp, void *func(), int argc, ...)
- Функция
makecontextустанавливает альтернативный поток управления вucp, предварительно инициализированный с помощьюgetcontext. Полеucp.uc_stackдолжно указывать на место для стека необходимого размера; обычно используется константаSIGSTKSZ. При совершении прыжка вucpс помощьюsetcontextилиswapcontextисполнение начинается с точки входа в функциюfuncс числом аргументовargc. При завершенииfuncуправление передаётсяucp.uc_link.
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
- Передаёт управление
ucpи сохраняет текущее состояние выполнения вoucp.
Remove ads
Пример
Суммиров вкратце
Перспектива
Пример ниже демонстрирует итератор, реализованный с помощью setcontext. Подобный код можно встретить достаточно редко; вместо использования setcontext для реализации кооперативной многозадачности часто используется различные библиотеки-обёртки, например, GNU Portable Threads.
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
/* Функция-итератор. Вход в неё осуществляется при первом вызове
* swapcontext, затем проходит в цикле от 0 до 9. Каждое значение сохраняется
* i_from_iterator, после чего производится возврат в основной цикл с помощью swapcontext.
* В основном цикле производится вывод значения и вызов swapcontext для возврата
* назад в функцию. При достижении конца цикла исполнение переключается на контекст main_context1*/
void loop(
ucontext_t *loop_context,
ucontext_t *other_context,
int *i_from_iterator)
{
int i;
for (i=0; i < 10; ++i) {
/* Запись счётчика цикла в место возврата итератора. */
*i_from_iterator = i;
/* Сохранение контекста цикла в ''loop_context'' и переключение на другой контекст. */
swapcontext(loop_context, other_context);
}
}
int main(void)
{
/* Три контекста:
* (1) main_context1 : указывает на main для возврата из цикла.
* (2) main_context2 : указывает на место переключения контекста в main
* (3) loop_context : указывает на место в цикле, в которое будет
* переходить управление из main. */
ucontext_t main_context1, main_context2, loop_context;
/* Стек для функции итератора. */
char iterator_stack[SIGSTKSZ];
/* Флаг, сообщающий о завершении итератора. */
volatile int iterator_finished;
/* Возвращаемое значение итератора. */
volatile int i_from_iterator;
/* Инициализация контекста итератора. uc_link указывает на main_context1,
* точку возврата при завершении итератора. */
loop_context.uc_link = &main_context1;
loop_context.uc_stack.ss_sp = iterator_stack;
loop_context.uc_stack.ss_size = sizeof(iterator_stack);
getcontext(&loop_context);
/* Заполнение loop_context, что позволяет swapcontext начать цикл.
* Преобразование в (void (*)(void)) необходимо для избежания предупреждения
* компилятора и не влияет на поведение функции. */
makecontext(&loop_context, (void (*)(void)) loop,
3, &loop_context, &main_context2, &i_from_iterator);
/* Очистка флага завершения. */
iterator_finished = 0;
/* Сохранения текущего контекста в main_context1. При завершении цикла
* управление будет возвращено в эту точку. */
getcontext(&main_context1);
if (!iterator_finished) {
/* Установка флага iterator_finished для отключения перезапуска итератора. */
iterator_finished = 1;
while (1) {
/* Сохранение этой точки в main_context2 и переключение на итератор.
* Первый вызов зачинает цикл, последующие осуществляют переключение
* через swapcontext в цикл. */
swapcontext(&main_context2, &loop_context);
printf("%d\n", i_from_iterator);
}
}
return 0;
}
Примечание: данный пример не соответствует справочной странице спецификации [1]. Функция makecontext требует, чтобы дополнительные параметры были типа int, а в примере передаются указатели. Это может привести к ошибке на 64-битных платформах (в частности, на архитектурах LP64, где sizeof(void*) > sizeof(int)). Теоретически эти проблемы могут быть решены, но эти решения также не являются портируемыми.
Remove ads
Примечания
Ссылки
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads