Asynchronously cancellable thread

Вызывающий поток может быть отменен в небезопасном состоянии

Описание

Этот дефект происходит, когда вы используете pthread_setcanceltype с аргументом PTHREAD_CANCEL_ASYNCHRONOUS установить cancellability тип вызывающего потока к асинхронному (или мгновенный). Асинхронно отменимый поток может быть отменен в любое время, обычно непосредственно после получения запроса отмены.

Риск

Вызывающий поток может быть отменен в небезопасном состоянии, которое могло привести к утечке ресурсов, мертвой блокировке, гонке данных, повреждению данных или непредсказуемому поведению.

Фиксация

Удалите вызов pthread_setcanceltype с аргументом PTHREAD_CANCEL_ASYNCHRONOUS чтобы использовать значение по умолчанию, cancellability вводят PTHREAD_CANCEL_DEFERRED вместо этого. Со значением по умолчанию cancellability тип, поток задерживает запросы отмены, пока это не вызывает функцию, которая является точкой отмены.

Примеры

развернуть все

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int fatal_error(void)
{
    exit(1);
}


volatile int a = 5;
volatile int b = 10;

pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

void* swap_values_thread(void* dummy)
{
    int i;
    int c;
    int result;
    if ((result =
             pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &i)) != 0) {
        /* handle error */
        fatal_error();
    }
    while (1) {
        if ((result = pthread_mutex_lock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
        c = b;
        b = a;
        a = c;
        if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
    }
    return NULL;
}

int main(void)
{
    int result;
    pthread_t worker;

    if ((result = pthread_create(&worker, NULL, swap_values_thread, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }

    /* Additional code */

    if ((result = pthread_cancel(worker)) != 0) {
        /* handle error */
        fatal_error();
    }


    if ((result = pthread_join(worker, 0)) != 0) {
        /* handle error */
        fatal_error();
    }

    if ((result = pthread_mutex_lock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }
    printf("a: %i | b: %i", a, b);
    if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }

    return 0;
}

В этом примере, cancellability типе worker поток установлен в асинхронный. Взаимное исключение global_lock помогает гарантировать что worker и main потоки не делают переменных доступа a и b одновременно. Однако worker распараллельте может быть отменен при содержании global_lock, и main поток никогда не будет получать global_lock, который приводит к мертвой блокировке.

Коррекция — использует тип Cancellability по умолчанию

Одна возможная коррекция должна удалить вызов pthread_setcanceltype. По умолчанию cancellability тип нового потока установлен в PTHREAD_CANCEL_DEFERRED. worker поток задерживает запросы отмены, пока он не вызывает функцию, которая является точкой отмены.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int fatal_error(void)
{
    exit(1);
}


volatile int a = 5;
volatile int b = 10;

pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

void* swap_values_thread(void* dummy)
{
    int i;
    int c;
    int result;
    while (1) {
        if ((result = pthread_mutex_lock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
        c = b;
        b = a;
        a = c;
        if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
    }
    return NULL;
}

int main(void)
{
    int result;
    pthread_t worker;

    if ((result = pthread_create(&worker, NULL, swap_values_thread, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }

    /* Additional code */

    if ((result = pthread_cancel(worker)) != 0) {
        /* handle error */
        fatal_error();
    }


    if ((result = pthread_join(worker, 0)) != 0) {
        /* handle error */
        fatal_error();
    }

    if ((result = pthread_mutex_lock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }
    printf("a: %i | b: %i", a, b);
    if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }

    return 0;
}

Проверяйте информацию

Группа: параллелизм
Язык: C | C++
Значение по умолчанию: 'off'
Синтаксис командной строки: ASYNCHRONOUSLY_CANCELLABLE_THREAD
Удар: носитель

Введенный в R2020a