Автоматическое обнаружение создания резьбы и критического сечения в Polyspace

С Polyspace®можно анализировать программы, где несколько потоков выполняются одновременно. Polyspace может анализировать ваш многозадачный код на гонки данных, взаимоблокировки и другие дефекты параллелизма, если анализу известно о модели параллелизма в вашем коде. В некоторых ситуациях Polyspace может автоматически обнаруживать создание потоков и критические разделы в вашем коде. Bug Finder обнаруживает их по умолчанию. В Code Prover вы включаете автоматическое обнаружение с помощью опции Enable automatic concurrency detection for Code Prover (-enable-concurrency-detection).

Рабочий процесс анализа многозадачного кода см. в разделе Анализ многозадачных программ в Polyspace.

Если функция создания потоков не обнаружена автоматически:

Многозадачные стандартные программы, которые может обнаружить Polyspace

Polyspace может обнаружить создание потоков и критические сечения, если вы используете примитивы из этих групп. Polyspace распознает вызовы этих стандартных программ как создание нового потока или как начало или конец критического раздела.

POSIX

Создание потоков: pthread_create

Начинается критический раздел: pthread_mutex_lock

Критические сечения заканчиваются: pthread_mutex_unlock

VxWorks

Создание потоков: taskSpawn

Начинается критический раздел: semTake

Критические сечения заканчиваются: semGive

Активация автоматического обнаружения примитивов параллелизма для VxWork®, в пользовательском интерфейсе десктопных продуктов Polyspace используйте VxWorks шаблон. Дополнительные сведения о шаблонах см. в разделе Создание проекта с использованием шаблона строении. В командной строке используйте следующие опции:

-D1=CPU=I80386
-D2=__GNUC__=2
-D3=__OS_VXWORKS

Обнаружение параллелизма возможно только в том случае, если функции многозадачности созданы из точки входа с именем main. Если точка входа имеет другое имя, например vxworks_entry_point, выполните одно из следующих действий:

  • Предоставьте main функция.

  • Preprocessor definitions (-D): В определениях предпроцессора задайте vxworks_entry_point=main.

Windows

Создание потоков: CreateThread

Начинается критический раздел: EnterCriticalSection

Критические сечения заканчиваются: LeaveCriticalSection

мкС/ОС II

Создание потоков: OSTaskCreate

Начинается критический раздел: OSMutexPend

Критические сечения заканчиваются: OSMutexPost

C++ 11

Создание потоков: std::thread::thread

Начинается критический раздел: std::mutex::lock

Критические сечения заканчиваются: std::mutex::unlock

Для автоопределения потоков C++ 11 явно задайте пути к файлам заголовка компилятора или используйтеpolyspace-configure.

Например, если вы используете std::thread для создания потоков явным образом укажите путь к папке, содержащей thread.h.

Смотрите также Ограничения автоматического обнаружения потоков.

C11

Создание потоков: thrd_create

Начинается критический раздел: mtx_lock

Критические сечения заканчиваются: mtx_unlock

Пример автоматического обнаружения потоков

Следующий многозадачный код моделирует пять философов, разделяющих пять вилок. В примере используется POSIX® стандартные программы создания потоков и иллюстрируют классический пример взаимоблокировки. Запустите Bug Finder на этом коде, чтобы увидеть тупик.

#include "pthread.h"
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t forks[5];

void* philo1(void* args)
{
    while (1) {
        printf("Philosopher 1 is thinking\n");
        sleep(1);
        pthread_mutex_lock(&forks[0]);
        printf("Philosopher 1 takes left fork\n");
        pthread_mutex_lock(&forks[1]);
        printf("Philosopher 1 takes right fork\n");
        printf("Philosopher 1 is eating\n");
        sleep(1);
        pthread_mutex_unlock(&forks[1]);
        printf("Philosopher 1 puts down right fork\n");
        pthread_mutex_unlock(&forks[0]);
        printf("Philosopher 1 puts down left fork\n");
    }
    return NULL;
}

void* philo2(void* args)
{
    while (1) {
        printf("Philosopher 2 is thinking\n");
        sleep(1);
        pthread_mutex_lock(&forks[1]);
        printf("Philosopher 2 takes left fork\n");
        pthread_mutex_lock(&forks[2]);
        printf("Philosopher 2 takes right fork\n");
        printf("Philosopher 2 is eating\n");
        sleep(1);
        pthread_mutex_unlock(&forks[2]);
        printf("Philosopher 2 puts down right fork\n");
        pthread_mutex_unlock(&forks[1]);
        printf("Philosopher 2 puts down left fork\n");
    }
    return NULL;
}

void* philo3(void* args)
{
    while (1) {
        printf("Philosopher 3 is thinking\n");
        sleep(1);
        pthread_mutex_lock(&forks[2]);
        printf("Philosopher 3 takes left fork\n");
        pthread_mutex_lock(&forks[3]);
        printf("Philosopher 3 takes right fork\n");
        printf("Philosopher 3 is eating\n");
        sleep(1);
        pthread_mutex_unlock(&forks[3]);
        printf("Philosopher 3 puts down right fork\n");
        pthread_mutex_unlock(&forks[2]);
        printf("Philosopher 3 puts down left fork\n");
    }
    return NULL;
}

void* philo4(void* args)
{
    while (1) {
        printf("Philosopher 4 is thinking\n");
        sleep(1);
        pthread_mutex_lock(&forks[3]);
        printf("Philosopher 4 takes left fork\n");
        pthread_mutex_lock(&forks[4]);
        printf("Philosopher 4 takes right fork\n");
        printf("Philosopher 4 is eating\n");
        sleep(1);
        pthread_mutex_unlock(&forks[4]);
        printf("Philosopher 4 puts down right fork\n");
        pthread_mutex_unlock(&forks[3]);
        printf("Philosopher 4 puts down left fork\n");
    }
    return NULL;
}

void* philo5(void* args)
{
    while (1) {
        printf("Philosopher 5 is thinking\n");
        sleep(1);
        pthread_mutex_lock(&forks[4]);
        printf("Philosopher 5 takes left fork\n");
        pthread_mutex_lock(&forks[0]);
        printf("Philosopher 5 takes right fork\n");
        printf("Philosopher 5 is eating\n");
        sleep(1);
        pthread_mutex_unlock(&forks[0]);
        printf("Philosopher 5 puts down right fork\n");
        pthread_mutex_unlock(&forks[4]);
        printf("Philosopher 5 puts down left fork\n");
    }
    return NULL;
}

int main(void)
{
    pthread_t ph[5];
    pthread_create(&ph[0], NULL, philo1, NULL);
    pthread_create(&ph[1], NULL, philo2, NULL);
    pthread_create(&ph[2], NULL, philo3, NULL);
    pthread_create(&ph[3], NULL, philo4, NULL);
    pthread_create(&ph[4], NULL, philo5, NULL);

    pthread_join(ph[0], NULL);
    pthread_join(ph[1], NULL);
    pthread_join(ph[2], NULL);
    pthread_join(ph[3], NULL);
    pthread_join(ph[4], NULL);
    return 1;
}

Каждому философу нужны две вилки для еды, правая и левая вилки. Функции philo1, philo2, philo3, philo4, и philo5 представлять философов. Каждая функция требует двух pthread_mutex_t ресурсы, представляющие две вилки, необходимые для еды. Все пять функций выполняются одновременно в пяти параллельных потоках.

Однако в этом примере происходит тупиковая ситуация. Когда каждый философ поднимает свою первую вилку (каждая нить блокирует одну pthread_mutex_t ресурс), все вилки используются. Итак, философы (нити) ждут своей второй вилки (второй pthread_mutex_t ресурс), чтобы стать доступным. Однако все вилки (ресурсы) удерживаются ожидающими философами (потоки), вызывая тупик.

Соглашение об именовании для автоматически обнаруженных потоков

Если вы используете функцию, такую как pthread_create() чтобы создать новые потоки (задачи), каждый поток связан с уникальным идентификатором. Например, в этом примере создаются два потока с идентификаторами id1 и id2.

pthread_t* id1, id2;

void main()
{
    pthread_create(id1, NULL, start_routine, NULL);
    pthread_create(id2, NULL, start_routine, NULL);
}

Если между потоками происходит гонка данных, анализ может обнаружить ее. При отображении результатов резьбы указываются как task_<reservedrangesplaceholder0 >, где id - идентификатор, сопоставленный с потоком. В предыдущем примере потоки идентифицируются как task_id1 и task_id2.

Если идентификатор потока:

  • Локально для функции, имя потока показывает функцию.

    Например, поток, созданный ниже, появляется следующим task_f:id

    void f(void)
    {
        pthread_t* id;
        pthread_create(id, NULL, start_routine, NULL);
    }
  • Поле структуры, имя потока показывает структуру.

    Например, поток, созданный ниже, появляется следующим task_a#id

    struct {pthread_t* id; int x;} a;
    pthread_create(a.id,NULL,start_routine,NULL);
    
  • Массив представителя, имя потока показывает массив.

    Например, поток, созданный ниже, появляется следующим task_tab[1].

    pthread_t* tab[10];
    pthread_create(tab[1],NULL,start_routine,NULL);
    

Если вы создаете два потока с различными идентификаторами потоков, но используете то же имя локальной переменной для идентификаторов потоков, имя второго потока изменяется, чтобы отличить его от первого потока. Для образца приведенные ниже потоки выглядят следующим task_func:id и task_func:id:1.

void func()
{
    {
        pthread_t id;
        pthread_create(&id, NULL, &task, NULL);

    }
    {
        pthread_t id;
        pthread_create(&id, NULL, &task, NULL);

    }
}

Ограничения автоматического обнаружения резьбы

Многозадачная модель, извлеченная Polyspace, не включает некоторые функции. Polyspace не может моделировать:

  • Приоритеты и атрибуты потока - проигнорированы Polyspace.

  • Рекурсивные семафоры.

  • Неограниченные идентификаторы потоков, такие как extern pthread_t ids[] - Предупреждение.

  • Вызовы примитива параллелизма через вызовы высокого порядка - Предупреждение.

  • Псевдонимы на идентификаторах потоков - Polyspace сверхаппроксимируется, когда используется псевдоним.

  • Разрыв потоков - Polyspace игнорирует pthread_join и thrd_join. Polyspace заменяет pthread_exit и thrd_exit стандартным выходом.

  • (Polyspace Bug Finder™ только) Создание нескольких потоков через несколько вызовов одной и той же функции с различными аргументами указателя.

     Пример

  • (Только для Polyspace Code Prover™) Общие локальные переменные - Только глобальные переменные считаются общими. Если к локальной переменной обращаются несколько потоков, анализ не учитывает общий характер переменной.

     Пример

  • (Только для Polyspace Code Prover) Общая динамическая память - Только глобальные переменные считаются общими. Если к динамически выделенной области памяти обращаются несколько потоков, анализ не учитывает ее общую природу.

     Пример

  • Количество задач, созданных с помощью CreateThread когда threadId установлено в NULL- Когда вы создаете несколько потоков, которые выполняют ту же функцию, если последний аргумент CreateThread является NULLPolyspace обнаруживает только один образец этой функции или задачи.

     Пример

  • (Только для C++ 11) Если во время создания потока в качестве начальных функций используются лямбда-выражения, Polyspace не обнаруживает общие переменные в лямбда-выражениях.

     Пример

  • (C++ 11 потоков только с Polyspace Code Prover) Строковые литералы как аргумент функции потока - Code Prover показывает красную ошибку Illegally dereferenced pointer, если функция потока имеет std::string& параметр и вы передаете строковый буквальный аргумент.

     Пример

См. также

|

Похожие темы