Описание
Разрушение заблокированного взаимного исключения происходит, когда задача уничтожает взаимное исключение после того, как это заблокировано (и прежде чем это будет разблокировано). Блокировка и разрушение могут произойти в той же задаче или различных задачах.
Риск
Взаимное исключение заблокировано, чтобы защитить совместно используемые переменные от параллельного доступа. Если взаимное исключение уничтожается в заблокированном состоянии, защита не применяется.
Фиксация
Чтобы зафиксировать этот дефект, уничтожьте взаимное исключение только после того, как вы разблокируете его. Это - хорошая практика проекта к:
Инициализируйте взаимное исключение прежде, чем создать потоки, где вы используете взаимное исключение.
Уничтожьте взаимное исключение после присоединения потоков, которые вы создали.
На панели Result Details вы видите два события, блокировку и разрушение взаимного исключения и задачи, которые инициировали события. Чтобы перейти к соответствующей строке в вашем исходном коде, кликните по событию.
Пример - блокировка и разрушение в различных задачах
#include <pthread.h>
pthread_mutex_t lock1;
pthread_mutex_t lock2;
pthread_mutex_t lock3;
void t0 (void) {
pthread_mutex_lock (&lock1);
pthread_mutex_lock (&lock2);
pthread_mutex_lock (&lock3);
pthread_mutex_unlock (&lock2);
pthread_mutex_unlock (&lock1);
pthread_mutex_unlock (&lock3);
}
void t1 (void) {
pthread_mutex_lock (&lock1);
pthread_mutex_lock (&lock2);
pthread_mutex_destroy (&lock3);
pthread_mutex_unlock (&lock2);
pthread_mutex_unlock (&lock1);
}
В этом примере после задачи t0 блокирует взаимное исключение lock3, задача, t1 может уничтожить его. Разрушение происходит, если следующие события происходят в последовательности:
t0 получает lock3.
t0 выпускает lock2.
t0 выпускает lock1.
t1 получает блокировку lock1, выпущенный t0.
t1 получает блокировку lock2, выпущенный t0.
t1 уничтожает lock3.
Для простоты этот пример использует соединение автоматического и ручного обнаружения параллелизма. Задачи t0 и t1 вручную заданы как точки входа при помощи опции Tasks (-entry-points).The критические разделы, реализованы через примитивы pthread_mutex_lock и pthread_mutex_unlock, который программное обеспечение обнаруживает автоматически. На практике, для спецификации точки входа (распараллеливают создание), вы будете использовать примитивы, такие как pthread_create. Следующий пример показывает, как дефект может появиться, когда вы используете pthread_create.
Исправление — блокировка места - разблокировала пару вместе в том же критическом разделе как разрушение
Блокировка и разрушение lock3 происходят в критическом разделе, наложенном lock1 и lock2, но разблокирование происходит снаружи. Одно возможное исправление должно поместить, блокировка - разблокировали пару в том же критическом разделе как разрушение взаимного исключения. Используйте один из этих критических разделов:
В этом исправленном коде блокировка - разблокировала пару, и разрушение помещается в критический раздел, наложенный lock1 и lock2. Когда t0 получает lock1 и lock2, t1 должен ожидать их релиза, прежде чем это выполнит инструкцию pthread_mutex_destroy (&lock3);. Поэтому t1 не может уничтожить взаимное исключение lock3 в заблокированном состоянии.
#include <pthread.h>
pthread_mutex_t lock1;
pthread_mutex_t lock2;
pthread_mutex_t lock3;
void t0 (void) {
pthread_mutex_lock (&lock1);
pthread_mutex_lock (&lock2);
pthread_mutex_lock (&lock3);
pthread_mutex_unlock (&lock3);
pthread_mutex_unlock (&lock2);
pthread_mutex_unlock (&lock1);
}
void t1 (void) {
pthread_mutex_lock (&lock1);
pthread_mutex_lock (&lock2);
pthread_mutex_destroy (&lock3);
pthread_mutex_unlock (&lock2);
pthread_mutex_unlock (&lock1);
}
Пример - блокировка и разрушение в стандартной программе запуска потока
#include <pthread.h>
/* Define globally accessible variables and a mutex */
#define NUMTHREADS 4
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
void atomic_operation(void);
void *do_create(void *arg) {
/* Creation thread */
pthread_mutex_init(&lock, NULL);
pthread_exit((void*) 0);
}
void *do_work(void *arg) {
/* Worker thread */
pthread_mutex_lock (&lock);
atomic_operation();
pthread_mutex_unlock (&lock);
pthread_exit((void*) 0);
}
void *do_destroy(void *arg) {
/* Destruction thread */
pthread_mutex_destroy(&lock);
pthread_exit((void*) 0);
}
int main (int argc, char *argv[]) {
int i;
void *status;
pthread_attr_t attr;
/* Create threads */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
/* Thread that initializes mutex */
pthread_create(&callThd[0], &attr, do_create, NULL);
/* Threads that use mutex for atomic operation*/
for(i=0; i<NUMTHREADS-1; i++) {
pthread_create(&callThd[i], &attr, do_work, (void *)i);
}
/* Thread that destroys mutex */
pthread_create(&callThd[NUMTHREADS -1], &attr, do_destroy, NULL);
pthread_attr_destroy(&attr);
/* Join threads */
for(i=0; i<NUMTHREADS; i++) {
pthread_join(callThd[i], &status);
}
pthread_exit(NULL);
}В этом примере создаются четыре потока. Потоки присвоены различные действия.
Первый поток callThd[0] инициализирует взаимное исключение lock.
Вторые и третьи потоки, callThd[1] и callThd[2], выполняют атомарную операцию, защищенную взаимным исключением lock.
Четвертый поток callThd[3] уничтожает взаимное исключение lock.
Потоки могут прервать друг друга. Поэтому сразу после того, как второй или третий поток блокирует взаимное исключение, четвертый поток может уничтожить его.
Исправление — инициализирует и уничтожает взаимное исключение внешняя стандартная программа запуска
Одно возможное исправление должно инициализировать и уничтожить взаимное исключение в функции main вне стандартной программы запуска потоков. Потоки выполняют только атомарную операцию. Вам нужно два меньше потоков, потому что взаимоисключающие потоки инициализации и разрушения не требуются.
#include <pthread.h>
/* Define globally accessible variables and a mutex */
#define NUMTHREADS 2
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
void atomic_operation(void);
void *do_work(void *arg) {
pthread_mutex_lock (&lock);
atomic_operation();
pthread_mutex_unlock (&lock);
pthread_exit((void*) 0);
}
int main (int argc, char *argv[]) {
int i;
void *status;
pthread_attr_t attr;
/* Create threads */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
/* Initialize mutex */
pthread_mutex_init(&lock, NULL);
for(i=0; i<NUMTHREADS; i++) {
pthread_create(&callThd[i], &attr, do_work, (void *)i);
}
pthread_attr_destroy(&attr);
/* Join threads */
for(i=0; i<NUMTHREADS; i++) {
pthread_join(callThd[i], &status);
}
/* Destroy mutex */
pthread_mutex_destroy(&lock);
pthread_exit(NULL);
}Исправление — использует второе взаимное исключение, чтобы защитить блокировку - разблокировали пару и разрушение
Другое возможное исправление должно использовать второе взаимное исключение и защитить блокировку - разблокировали пару от разрушения. Этот исправленный код использует взаимное исключение lock2, чтобы достигнуть этой защиты. Второе взаимное исключение инициализируется в функции main вне стандартной программы запуска потоков.
#include <pthread.h>
/* Define globally accessible variables and a mutex */
#define NUMTHREADS 4
pthread_t callThd[NUMTHREADS];
pthread_mutex_t lock;
pthread_mutex_t lock2;
void atomic_operation(void);
void *do_create(void *arg) {
/* Creation thread */
pthread_mutex_init(&lock, NULL);
pthread_exit((void*) 0);
}
void *do_work(void *arg) {
/* Worker thread */
pthread_mutex_lock (&lock2);
pthread_mutex_lock (&lock);
atomic_operation();
pthread_mutex_unlock (&lock);
pthread_mutex_unlock (&lock2);
pthread_exit((void*) 0);
}
void *do_destroy(void *arg) {
/* Destruction thread */
pthread_mutex_lock (&lock2);
pthread_mutex_destroy(&lock);
pthread_mutex_unlock (&lock2);
pthread_exit((void*) 0);
}
int main (int argc, char *argv[]) {
int i;
void *status;
pthread_attr_t attr;
/* Create threads */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
/* Initialize second mutex */
pthread_mutex_init(&lock2, NULL);
/* Thread that initializes first mutex */
pthread_create(&callThd[0], &attr, do_create, NULL);
/* Threads that use first mutex for atomic operation */
/* The threads use second mutex to protect first from destruction in locked state*/
for(i=0; i<NUMTHREADS-1; i++) {
pthread_create(&callThd[i], &attr, do_work, (void *)i);
}
/* Thread that destroys first mutex */
/* The thread uses the second mutex to prevent destruction of locked mutex */
pthread_create(&callThd[NUMTHREADS -1], &attr, do_destroy, NULL);
pthread_attr_destroy(&attr);
/* Join threads */
for(i=0; i<NUMTHREADS; i++) {
pthread_join(callThd[i], &status);
}
/* Destroy second mutex */
pthread_mutex_destroy(&lock2);
pthread_exit(NULL);
}