ПроблемаГонка данных происходит, когда:
Чтобы найти этот дефект, перед анализом необходимо задать опции многозадачности. Чтобы задать эти опции, на панели Configuration выберите Multitasking. Для получения дополнительной информации смотрите Настройка многозадачного анализа Polyspace вручную.
РискГонка данных может привести к непредсказуемым значениям общей переменной, потому что вы не управляете порядком операций в различных задачах.
Гонки данных между двумя операциями записи более серьезны, чем гонки данных между операцией записи и чтения. Две операции записи могут мешать друг другу и приводить к неопределенным значениям. Чтобы идентифицировать конфликты записи и записи, используйте фильтры в 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
. Дефект Data race возникает из-за операции 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;
}