Double unlock

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

Описание

Этот дефект возникает, когда:

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

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

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

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

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

Риск

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

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

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

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

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

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

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

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

Примеры

расширить все



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)Starting routineEnding routine
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++
По умолчанию: On
Синтаксис командной строки: DOUBLE_UNLOCK
Влияние: Высокий
ИДЕНТИФИКАТОР CWE: 765
Введенный в R2014b