РискГонка данных может привести к непредсказуемым значениям общей переменной, поскольку вы не контролируете порядок операций в различных задачах.
Гонки данных между двумя операциями записи более серьезны, чем гонки данных между операцией записи и операцией чтения. Две операции записи могут создавать помехи друг другу и приводить к неопределенным значениям. Чтобы определить конфликты записи, используйте фильтры в столбце Подробности (Detail) панели Список результатов (Results List). Для этих конфликтов в столбце Подробно (Detail) отображается дополнительная строка:
Variable value may be altered by write-write concurrent access.
См. раздел
Фильтрация и группирование результатов в пользовательском интерфейсе Polyspace Desktop.
ЗафиксироватьЧтобы устранить этот дефект, защитите операции над общей переменной с помощью критических разделов, временного исключения или другого средства. См. раздел Защита общих переменных в многозадачном коде.
Чтобы определить существующие защиты, которые можно использовать повторно, см. таблицу и графики, связанные с результатом. В таблице показаны все пары конфликтующих вызовов. В столбце 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;
}В этом примере Bug Finder обнаруживает создание отдельных потоков с помощью pthread_create. Дефект гонки данных возникает из-за операции count = count + 1 в потоке с идентификатором thread_increment конфликтует с операцией c = count в потоке с идентификатором thread_get. Переменная count используется в нескольких потоках без общей защиты.
Две конфликтующие операции являются неатомными. Операция c = count неатомен на 32-разрядных мишенях. См. раздел Определение атомных операций в многозадачном коде.
Исправление - защита операций с помощью pthread_mutex_lock и pthread_mutex_unlock ПараПредотвращение параллельного доступа к переменной 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;
}