| 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 |