Описание
Гонка данных происходит когда:
Чтобы найти этот дефект, необходимо задать многозадачные опции перед анализом. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking. Для получения дополнительной информации смотрите Анализ Многозадачности Polyspace Конфигурирования Вручную.
Риск
Гонка данных может привести к непредсказуемым значениям совместно используемой переменной, потому что вы не управляете порядком операций в различных задачах.
Гонки данных между двумя операциями записи более серьезны, чем гонки данных между записью и операцией чтения. Две операции записи могут вмешаться друг в друга и привести к неопределенным значениям. Чтобы идентифицировать конфликты записи записи, используйте фильтры на столбце Detail панели Results List. Для этих конфликтов столбец Detail показывает дополнительную строку:
Variable value may be altered by write-write concurrent access.
Смотрите
результаты фильтра и группы.
Фиксация
Чтобы зафиксировать этот дефект, защитите операции на совместно используемой переменной с помощью критических разделов, временного исключения, или другой имеет в виду. Смотрите Меры защиты для Совместно используемых переменных в Многозадачном Коде.
Чтобы идентифицировать существующие меры защиты, которые можно снова использовать, см. таблицу и графики, сопоставленные с результатом. Таблица показывает каждую пару конфликтных вызовов. Столбец Access Protections показывает существующие меры защиты на вызовах. Чтобы видеть, что последовательность вызова функции ведет к конфликтам, кликните по значку. Для примера смотрите ниже.
Пример - незащищенная операция на глобальной переменной от нескольких задач
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
В этом примере, чтобы эмулировать многозадачное поведение, задают следующие опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-entry-points task1,task2,task3
-critical-section-begin begin_critical_section:cs1
-critical-section-end end_critical_section:cs1
В этом примере задачи task1
, task2
и task3
вызывают функциональный increment
. increment
содержит операцию var++
, которая может включить несколько машинных команд включая:
Эти машинные команды, когда выполняется от task1
и task2
, могут произойти одновременно в непредсказуемой последовательности. Например, чтение var
от task1
может произойти или прежде или после записи в var
от task2
. Поэтому значение var
может быть непредсказуемым.
Хотя task3
вызывает increment
в критическом разделе, другие задачи не используют тот же критический раздел. Операции в критическом разделе task3
не являются взаимоисключающими с операциями в других задачах.
Поэтому эти три задачи работают с совместно используемой переменной без общей защиты. В ваших деталях результата вы видите каждую пару конфликтных вызовов функции.
Если вы кликаете по значку, вы видите, что последовательность вызова функции запускается от точки входа до операции чтения или операции записи. Вы также видите, что операция, начинающая с task3
, находится в критическом разделе. Запись Access Protections показывает блокировку, и разблокируйте функцию, которые начинают и заканчивают критический раздел. В этом примере вы видите функции begin_critical_section
и end_critical_section
.
Исправление — операция места в критическом разделе
Одно возможное исправление должно поместить операцию в критический раздел. Можно реализовать критический раздел несколькими способами. Например:
Можно поместить var++
в критический раздел. Когда task1
вводит свой критический раздел, другие задачи не могут ввести свои критические разделы, пока task1
не оставляет свой критический раздел. Операция var++
от этих трех задач не может вмешаться друг в друга.
Чтобы реализовать критический раздел, в функциональном increment
, помещают операцию var++
между вызовами begin_critical_section
и end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
begin_critical_section();
var++;
end_critical_section();
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
increment();
}
Можно поместить вызов increment
в том же критическом разделе в этих трех задачах. Когда task1
вводит свой критический раздел, другие задачи не могут ввести свои критические разделы, пока task1
не оставляет свой критический раздел. Вызовы increment
от этих трех задач не могут вмешаться друг в друга.
Реализовывать критический раздел, в каждой из этих трех задач, increment
вызова между вызовами begin_critical_section
и end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task2(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
Исправление — делает задачи временно исключительными
Другое возможное исправление должно сделать задачи, task1
, task2
и task3
, временно исключительный. Временно исключительные задачи не могут выполниться одновременно.
На панели Configuration задайте следующие дополнительные опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-temporal-exclusions-file "C:\exclusions_file.txt"
где файл
C:\exclusions_file.txt
имеет следующую строку:
Пример - незащищенная операция в потоках, созданных с pthread_create
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void* increment_count(void* args)
{
count = count + 1;
return NULL;
}
void* set_count(void *args)
{
long long c;
c = count;
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}
В этом примере Средство поиска Ошибки обнаруживает создание, разделяют потоки pthread_create
. Дефект Data race повышен, потому что операция count = count + 1
в потоке с ID thread_increment
конфликтует с операцией c = count
в потоке с ID thread_get
. К переменной count
получают доступ в нескольких потоках без общей защиты.
Две конфликтных операции являются неатомарными. Операция c = count
является неатомарной на 32-битных целях. Смотрите Задают Атомарные Операции в Многозадачном Коде.
Исправление — защищает операции с парой pthread_mutex_unlock
и pthread_mutex_lock
Чтобы предотвратить параллельный доступ на переменной count
, защитите операции на count
с критическим разделом. Используйте функции pthread_mutex_lock
и pthread_mutex_unlock
, чтобы реализовать критический раздел.
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void* increment_count(void* args)
{
pthread_mutex_lock(&count_mutex);
count = count + 1;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
void* set_count(void *args)
{
long long c;
pthread_mutex_lock(&count_mutex);
c = count;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}