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 |