exponenta event banner

Блокирование при удержании блокировки

Задача выполняет длительную операцию, удерживая блокировку

Описание

Этот дефект возникает, когда задача (поток) выполняет потенциально длительную операцию при удержании блокировки.

Средство проверки рассматривает вызовы этих функций как потенциально длительные:

  • Функции, получающие доступ к сети, такие как recv

  • Функции вызова системы, такие как fork, pipe и system

  • Функции для операций ввода-вывода, такие как getchar и scanf

  • Функции обработки файлов, такие как fopen, remove и lstat

  • Функции управления каталогами, такие как mkdir и rmdir

Средство проверки автоматически обнаруживает определенные примитивы, которые удерживают и снимают блокировку, например, pthread_mutex_lock и pthread_mutex_unlock. Полный список автоматически обнаруживаемых примитивов см. в разделе Автоматическое обнаружение создания потоков и критического сечения в Polyspace.

Риск

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

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

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

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

Примеры

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

#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
 
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

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

Исправление - Выполнение операции блокирования перед получением блокировки

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

#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
  
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
    /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

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

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