ПроблемаЭта проблема возникает при использовании pthread_setcanceltype
с аргументом PTHREAD_CANCEL_ASYNCHRONOUS
для установки типа отменяемости потока вызова на асинхронный (или немедленный). Асинхронно отменяемый поток может быть отменен в любое время, обычно сразу после получения запроса на отмену.
РискВызывающий поток может быть отменен в небезопасном состоянии, что может привести к утечке ресурсов, взаимоблокировке, гонке данных, повреждению данных или непредсказуемому поведению.
ЗафиксироватьУдалите вызов, чтобы pthread_setcanceltype
с аргументом PTHREAD_CANCEL_ASYNCHRONOUS
для использования типа отменяемости по умолчанию PTHREAD_CANCEL_DEFERRED
вместо этого. С типом отменяемости по умолчанию поток откладывает запросы на отмену, пока не вызывает функцию, которая является точкой отмены.
Пример - Тип отменяемости потока, установленного на Asynchronous#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;
}
В этом примере тип отменяемости worker
поток установлен в асинхронный режим. Мьютекс- global_lock
помогает гарантировать, что worker
и main
потоки не имеют доступа к переменным a
и b
одновременно. Однако worker
поток может быть отменен при удержании global_lock
, и main
поток никогда не получит global_lock
, что приводит к тупику.
Коррекция - используйте тип отменяемости по умолчаниюОдной из возможных коррекций является удаление вызова на pthread_setcanceltype
. По умолчанию для типа отменяемости нового потока задано значение PTHREAD_CANCEL_DEFERRED
. The 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;
}