CERT C: Rec. CON05-C

Не выполнять операции, которые могут блокироваться при удержании блокировки

Описание

Определение правила

Не выполнять операции, которые могут блокироваться при удержании блокировки.[1]

Реализация Polyspace

Эта проверка проверяет наличие операции блокировки при удержании блокировки.

Примеры

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

Проблема

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

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

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

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

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

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

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

Шашка автоматически обнаруживает определенные примитивы, которые удерживают и отпускают блокировку, например pthread_mutex_lock и pthread_mutex_unlock. Полный список примитивов, которые автоматически обнаруживаются, см. в разделе «Автоматическое обнаружение создания потоков» и «Критическое сечение» в Polyspace.

Риск

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

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

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

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

Пример - Операции ввода-вывода сети с 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;
}

Проверяйте информацию

Группа: Rec. 14. Параллелизм (CON)
Введенный в R2019a

[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.