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