Destruction of locked mutex

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

Описание

Этот дефект происходит, когда задача уничтожает взаимное исключение после того, как это заблокировано (и прежде чем это будет разблокировано). Блокировка и разрушение могут произойти в той же задаче или различных задачах.

Риск

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

Фиксация

Чтобы зафиксировать этот дефект, уничтожьте взаимное исключение только после того, как вы разблокируете его. Это - хорошая практика проекта к:

  • Инициализируйте взаимное исключение прежде, чем создать потоки, где вы используете взаимное исключение.

  • Уничтожьте взаимное исключение после присоединения потоков, которые вы создали.

На панели 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 может уничтожить его. Разрушение происходит, если следующие события происходят в последовательности:

  1. t0 получает lock3.

  2. t0 релизы lock2.

  3. t0 релизы lock1.

  4. t1 получает блокировку lock1 выпущенный t0.

  5. t1 получает блокировку lock2 выпущенный t0.

  6. t1 уничтожает lock3.

Для простоты этот пример использует соединение автоматического и ручного обнаружения параллелизма. Задачи t0 и t1 вручную заданы как точки входа при помощи опции Tasks (-entry-points). Критические разделы реализованы через примитивы pthread_mutex_lock и pthread_mutex_unlock то, что программное обеспечение обнаруживает автоматически. На практике, для спецификации точки входа (распараллеливают создание), вы будете использовать примитивы, такие как pthread_create. Следующий пример показывает, как дефект может появиться, когда вы используете pthread_create.

Коррекция — блокировка места - разблокировала пару вместе в том же критическом разделе как разрушение

Блокировка и разрушение lock3 происходит в критическом разделе, наложенном lock1 и lock2, но разблокирование происходит снаружи. Одна возможная коррекция должна поместить, блокировка - разблокировали пару в том же критическом разделе как разрушение взаимного исключения. Используйте один из этих критических разделов:

  • Критический раздел наложен lock1 один.

  • Критический раздел наложен lock1 и lock2.

В этом исправленном коде блокировка - разблокировала пару, и разрушение помещается в критический раздел, наложенный lock1 и lock2. Когда t0 получает lock1 и lock2t1 должен ожидать их релиза, прежде чем он выполнит инструкцию 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);
}

Информация о результате

Группа: параллелизм
Язык: C | C++
Значение по умолчанию: Off
Синтаксис командной строки: DESTROY_LOCKED
Удар: Средняя
ID CWE: 667, 826
Введенный в R2017b