exponenta event banner

Двойная разблокировка

Функция разблокировки вызывается дважды в задаче без промежуточного вызова функции блокировки

Описание

Этот дефект возникает в следующих случаях:

  • Задача вызывает функцию блокировки my_lock.

  • Задача вызывает соответствующую функцию разблокировки. my_unlock.

  • Вызов задачи my_unlock снова. Задача не вызывается my_lock второй раз между двумя вызовами my_unlock.

В многозадачном коде функция блокировки начинает критический раздел кода и функция разблокировки заканчивает его. Когда задача task1 вызывает функцию блокировки my_lock, другие задачи, вызывающие my_lock должен подождать до task1 вызывает соответствующую функцию разблокировки. Polyspace ® требует, чтобы функции блокировки и разблокировки имели формуvoid func(void).

Чтобы найти этот дефект, перед анализом необходимо указать параметры многозадачности. Чтобы задать эти параметры, на панели Конфигурация выберите Многозадачность.

Риск

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

Зафиксировать

Исправление зависит от первопричины дефекта.

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

См. примеры исправлений ниже. Чтобы избежать проблемы, можно следовать практике вызова функций блокировки и разблокировки в одном модуле на одном уровне абстракции. Например, в этом примере func вызывает функцию блокировки и разблокировки на одном уровне, но func2 не делает.

void func() {
  my_lock();
  {
    ...
  }
  my_unlock();
}

void func2() {
  {
   my_lock();
   ...
  }
  my_unlock();
}

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

Примеры

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



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

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

ВыборСтоимость
Configure multitasking manually
Tasks (-entry-points)

task1

task2

Critical section details (-critical-section-begin -critical-section-end)Запуск подпрограммыЗавершение процедуры
BEGIN_CRITICAL_SECTIONEND_CRITICAL_SECTION

В командной строке можно использовать следующее:

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin BEGIN_CRITICAL_SECTION:cs1
   -critical-section-end END_CRITICAL_SECTION:cs1

task1 входит в критический раздел через вызов BEGIN_CRITICAL_SECTION();. task1 оставляет критический раздел через вызов END_CRITICAL_SECTION();. task1 требования END_CRITICAL_SECTION снова без промежуточного вызова BEGIN_CRITICAL_SECTION.

Коррекция - снять вторую разблокировку

Если вы хотите второй global_var+=1; чтобы находиться вне критического раздела, одной из возможных корректировок является удаление второго вызова END_CRITICAL_SECTION. Однако, если используются другие задачи global_var, этот код может создать Data race ошибка.



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    global_var += 1;
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}
Исправление - Снять первую разблокировку

Если вы хотите второй global_var+=1; чтобы быть внутри критического раздела, одной из возможных корректировок является удаление первого вызова END_CRITICAL_SECTION.



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}
Исправление - Добавить другую блокировку

Если вы хотите второй global_var+=1; чтобы быть внутри критического раздела, другая возможная коррекция состоит в добавлении другого вызова BEGIN_CRITICAL_SECTION.



int global_var;

void BEGIN_CRITICAL_SECTION(void);
void END_CRITICAL_SECTION(void);

void task1(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

void task2(void)
{
    BEGIN_CRITICAL_SECTION();
    global_var += 1;
    END_CRITICAL_SECTION();
}

Информация о результатах

Группа: Параллелизм
Язык: C | C++
По умолчанию: Вкл.
Синтаксис командной строки: DOUBLE_UNLOCK
Воздействие: Высокое
ИДЕНТИФИКАТОР CWE: 765
Представлен в R2014b