exponenta event banner

Двойной замок

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

Описание

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

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

  • Вызов задачи my_lock перед вызовом соответствующей функции разблокировки.

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

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

Риск

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

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

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

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

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

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

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

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

Примеры

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



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    lock(); 
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

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

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

task1

task2

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

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

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin lock:cs1
   -critical-section-end unlock:cs1

task1 входит в критический раздел через вызов lock();. task1 требования lock перед тем, как он покинет критический участок через вызов unlock();.

Коррекция - снять первый замок

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



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    global_var += 1;
    lock(); 
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}
Коррекция - снять вторую блокировку

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



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}
Исправление - Добавить еще одну разблокировку

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



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    unlock();
    lock();
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}


int global_var;

void lock(void);
void unlock(void);

void performOperation(void) {
  lock();
  global_var++;
}

void task1(void)
{
    lock();
    global_var += 1;
    performOperation();
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

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

ВыборСпецификация
Configure multitasking manually
Tasks (-entry-points)

task1

task2

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

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

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin lock:cs1
   -critical-section-end unlock:cs1

task1 входит в критический раздел через вызов lock();. task1 вызывает функцию performOperation. В performOperation, lock вызывается снова, даже если task1 не покинул критический участок через вызов unlock();.

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

  • Входит в функцию performOperation.

  • Внутри performOperation, пытается снова войти в ту же самую критическую секцию.

Можно щелкнуть по каждому событию для перехода к соответствующей строке в исходном коде.

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

Одной из возможных корректировок является удаление вызова для lock в task1.



int global_var;

void lock(void);
void unlock(void);

void performOperation(void) {
  global_var++;
}

void task1(void)
{
    lock();
    global_var += 1;
    performOperation();
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

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

Группа: Параллелизм
Язык: C | C++
По умолчанию: Вкл.
Синтаксис командной строки: DOUBLE_LOCK
Воздействие: Высокое
CWE ID: 764
Представлен в R2014b