ПроблемаОперация блокировки при удержании блокировки происходит, когда задача (поток) выполняет потенциально длительную операцию при удержании блокировки.
Средство проверки рассматривает вызовы этих функций как потенциально длительные:
Функции, получающие доступ к сети, такие как 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;
}