| PTHREAD_CLEANUP_PUSH(3) | Руководство программиста Linux | PTHREAD_CLEANUP_PUSH(3) |
pthread_cleanup_push, pthread_cleanup_pop - помещает и выталкивает очищающие обработчики при отмене нити
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),
void *arg);
void pthread_cleanup_pop(int execute);
Компилируется и компонуется вместе с -pthread.
Данные функции управляют стеком нити, вызывающим очищающие обработчики при отмене нити. Очищающий обработчик — это функция, которая автоматически вызывается при отмене нити (или в при других обстоятельствах, описанных ниже); это может быть, например, отмена блокировки мьютекса, при которой он становится доступным другим нитям процесса.
Функция pthread_cleanup_push() помещает обработчик routine наверх стека очищающих обработчиков. Позднее, при вызове routine, ему будет передан arg в качестве аргумента.
Функция pthread_cleanup_pop() удаляет обработчик с вершины стека очищающих обработчиков, и, возможно, выполняет его, если execute не равно нулю.
Очищающий обработчик при отмене выталкивается из стека и выполняется при следующих условиях:
В POSIX.1 допускается реализация pthread_cleanup_push() и pthread_cleanup_pop() в виде макросов, которые раскрывают текст, содержащий '{' и '}', соответственно. По этой причине, вызывающий должен убедиться, что вызовы этих функций используются парно в одной функции и на одном лексическом уровне вложенности (другими словами, очищающий обработчик устанавливается только во время выполнения определённой секции кода).
Вызов longjmp(3) (siglongjmp(3)) приводит к непредсказуемым результатам, если был сделан любой вызов в pthread_cleanup_push() или pthread_cleanup_pop() без соответствующего вызову пары, так как буфер перехода (jump buffer) был заполнен setjmp(3) (sigsetjmp(3)). Подобным образом, вызов longjmp(3) (siglongjmp(3)) изнутри очищающего обработчика приводит к непредсказуемым результатам, если буфер перехода также не был заполнен setjmp(3) (sigsetjmp(3)) внутри обработчика.
Данные функции не возвращают никаких значений.
Эти функции не вызывают ошибок.
Описание терминов данного раздела смотрите в attributes(7).
| Интерфейс | Атрибут | Значение |
| pthread_cleanup_push(), pthread_cleanup_pop() | Безвредность в нитях | MT-Safe |
POSIX.1-2001, POSIX.1-2008.
В Linux, функции pthread_cleanup_push() и pthread_cleanup_pop() реализованы в виде макросов, которые расширяются до текста, содержащего '{' и '}', соответственно. Это означает, что переменные, объявленные внутри области парных вызовов этих функций, будут видимы внутри только этой области.
В POSIX.1 сказано, что результат использования return, break, continue или goto для преждевременного оставления блока, окружающего pthread_cleanup_push() и pthread_cleanup_pop(), не определён. В переносимых приложениях не нужно так делать.
Далее показан простой пример использования функций, описанных на этой странице. Программа создаёт нить, которая выполняет цикл обрамлённый pthread_cleanup_push() и pthread_cleanup_pop(). В этом цикле каждую секунду увеличивается глобальная переменная cnt. В зависимости от аргументов командной строки главная нить посылает другой нити запрос на отмену или изменяет глобальную переменную, что заставляет другую нить выйти из цикла и завершить работу (с помощью return).
В этом сеансе главная нить посылает запрос отмены другой нити:
$ ./a.out Запущена новая нить cnt = 0 cnt = 1 Отменяем нить Вызван очищающий обработчик Нить отменена; cnt = 0
Здесь мы видим, что нить была отменена и что был вызван очищающий обработчик и он сбросил значение глобальной переменной cnt в 0.
В следующем сеансе главная программа изменяет глобальную переменную, что вызывает штатное завершение другой нити:
$ ./a.out x Запущена новая нить cnt = 0 cnt = 1 Нить завершилась штатным образом; cnt = 2
Здесь мы видим, что очищающий обработчик не вызывался (так как cleanup_pop_arg равно 0), и поэтому значение cnt не сброшено.
В следующем сеансе главная программа изменяет глобальную переменную, что вызывает штатное завершение другой нити, передаёт ненулевое значение в cleanup_pop_arg:
$ ./a.out x 1 Запущена новая нить cnt = 0 cnt = 1 Вызван очищающий обработчик Нить завершилась штатным образом; cnt = 0
Здесь мы видим, что хотя нить не отменена, всё же вызван очищающий обработчик, так как в pthread_cleanup_pop() передан ненулевой аргумент.
#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static int done = 0;
static int cleanup_pop_arg = 0;
static int cnt = 0;
static void
cleanup_handler(void *arg)
{
printf("Вызван очищающий обработчик\n");
cnt = 0;
}
static void *
thread_start(void *arg)
{
time_t start, curr;
printf("Запущена новая нить\n");
pthread_cleanup_push(cleanup_handler, NULL);
curr = start = time(NULL);
while (!done) {
pthread_testcancel(); /* точка отмены */
if (curr < time(NULL)) {
curr = time(NULL);
printf("cnt = %d\n", cnt); /* точка отмены */
cnt++;
}
}
pthread_cleanup_pop(cleanup_pop_arg);
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t thr;
int s;
void *res;
s = pthread_create(&thr, NULL, thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
sleep(2); /* даём время нити запуститься */
if (argc > 1) {
if (argc > 2)
cleanup_pop_arg = atoi(argv[2]);
done = 1;
} else {
printf("Отменяем нить\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
}
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("Нить была отменена; cnt = %d\n", cnt);
else
printf("Нить завершилась штатным образом; cnt = %d\n", cnt);
exit(EXIT_SUCCESS);
}
pthread_cancel(3), pthread_cleanup_push_defer_np(3), pthread_setcancelstate(3), pthread_testcancel(3), pthreads(7)
| 2019-03-06 | Linux |