CERT C: Rule CON31-C

Не уничтожайте взаимное исключение, в то время как оно заблокировано

Описание

Управляйте определением

Не уничтожайте взаимное исключение, в то время как оно заблокировано. [1]

Реализация Polyspace

Это средство проверки проверяет на Разрушение заблокированного взаимного исключения.

Примеры

развернуть все

Проблема

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

Риск

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

Фиксация

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

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

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

На панели 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);
}

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

Группа: правило 14. Параллелизм (CON)

Введенный в R2019a


[1]  Это программное обеспечение было создано MathWorks, включающим фрагменты: “Веб-сайт SEI CERT-C”, © 2017 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

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