Blocking operation while holding lock

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

Описание

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

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

  • Функции, которые получают доступ к сети, такие как 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++
По умолчанию: Off
Синтаксис командной строки : BLOCKING_WHILE_LOCKED
Влияние: Низкое
ИДЕНТИФИКАТОР CWE : 667
Введенный в R2018b