ПроблемаЭта проблема возникает, когда несколько потоков используют более одного мьютекса для параллельного ожидания одной и той же переменной условия. Поток ожидает переменной условия, вызывая функции pthread_cond_timedwait
или pthread_cond_wait
. Эти функции берут переменную условия и заблокированный мьютекс в качестве аргументов, и переменная условия связана с этим мьютексом, когда поток ожидает от переменной условия.
Шашки помечают использование pthread_cond_timedwait
или pthread_cond_wait
в одной из ниток. Смотрите столбец Event на панели Results Details, чтобы просмотреть потоки, ожидающие той же переменной условия и использующие другой мьютекс.
РискКогда поток ожидает переменной условия с помощью мьютекса, переменная условия связана с этим мьютексом. Любой другой поток, использующий другой мьютекс, чтобы ждать от той же переменной условия, является неопределенным поведением в соответствии со стандартом POSIX.
ЗафиксироватьИспользуйте тот же аргумент mutex для pthread_cond_timedwait
или pthread_cond_wait
когда потоки одновременно ожидают одной и той же переменной условия или используют отдельные переменные условия для каждого мьютекса.
Пример - Параллельное ожидание переменной условия с несколькими мьютексами#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define Thrd_return_t void *
#define __USE_XOPEN2K8
#define COUNT_LIMIT 5
static void fatal_error(void)
{
exit(1);
}
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_mutex_t mutex3;
pthread_cond_t cv;
int count1 = 0, count2 = 0, count3 = 0;
#define DELAY 8
Thrd_return_t waiter1(void* arg)
{
int ret;
while (count1 < COUNT_LIMIT) {
if ((ret = pthread_mutex_lock(&mutex1)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex1)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count1 = %d\n", ++count1);
if ((ret = pthread_mutex_unlock(&mutex1)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t waiter2(void* arg)
{
int ret;
while (count2 < COUNT_LIMIT) {
if ((ret = pthread_mutex_lock(&mutex2)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex2)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count2 = %d\n", ++count2);
if ((ret = pthread_mutex_unlock(&mutex2)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t signaler(void* arg)
{
int ret;
while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) {
sleep(1);
printf("signaling\n");
if ((ret = pthread_cond_broadcast(&cv)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t waiter3(void* arg)
{
int ret;
while (count3 % COUNT_LIMIT != 0) {
if ((ret = pthread_mutex_lock(&mutex3)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex3)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count3 = %d\n", ++count3);
if ((ret = pthread_mutex_unlock(&mutex3)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
int main(void)
{
int ret;
pthread_t thread1, thread2, thread3;
pthread_mutexattr_t attr;
if ((ret = pthread_mutexattr_init(&attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex1, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex2, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex3, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_cond_init(&cv, NULL)) != 0) {
/* handle error */
fatal_error();
}
if ((ret = pthread_create(&thread1, NULL, &waiter1, NULL))) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_create(&thread2, NULL, &waiter2, NULL))) {
/* handle error */
fatal_error();
}
if ((ret = pthread_create(&thread3, NULL, &signaler, NULL))) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread1, NULL)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread2, NULL)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread3, NULL)) != 0) {
/* Handle error */
fatal_error();
}
while (1) { ; }
return 0;
}
В этом примере для защиты каждой count
используется другой мьютекс
переменная. Начиная со всех трех waiter
функции ожидают от той же переменной условия cv
с различными мьютексами, вызов pthread_cond_wait
будет успешным для одного из потоков, и вызов будет неопределенным для двух других.
Шашка поднимает дефект для функции waiter3
даже если функция не вызывается прямо или косвенно потоком, точкой входа или прерыванием. Анализ рассматривает функцию waiter3
вызывается основной программой через адрес ее функции или неопознанный поток, создание которого является отсутствующим исходным кодом.
Коррекция - используйте тот же мутекс для всех потоков, ожидающих на той же переменной условияОдной из возможных коррекций является передача того же аргумента мьютекса всему вызову pthread_cond_wait
которые используются для ожидания той же переменной условия.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define Thrd_return_t void *
#define __USE_XOPEN2K8
#define COUNT_LIMIT 5
static void fatal_error(void)
{
exit(1);
}
pthread_mutex_t mutex;
pthread_cond_t cv;
int count1 = 0, count2 = 0, count3 = 0;
#define DELAY 8
Thrd_return_t waiter1(void* arg)
{
int ret;
while (count1 < COUNT_LIMIT) {
if ((ret = pthread_mutex_lock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count1 = %d\n", ++count1);
if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t waiter2(void* arg)
{
int ret;
while (count2 < COUNT_LIMIT) {
if ((ret = pthread_mutex_lock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count2 = %d\n", ++count2);
if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t signaler(void* arg)
{
int ret;
while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) {
sleep(1);
printf("signaling\n");
if ((ret = pthread_cond_broadcast(&cv)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
Thrd_return_t waiter3(void* arg)
{
int ret;
while (count3 % COUNT_LIMIT != 0) {
if ((ret = pthread_mutex_lock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret =
pthread_cond_wait(&cv, &mutex)) != 0) {
/* Handle error */
fatal_error();
}
sleep(random() % DELAY);
printf("count3 = %d\n", ++count3);
if ((ret = pthread_mutex_unlock(&mutex)) != 0) {
/* Handle error */
fatal_error();
}
}
return (Thrd_return_t)0;
}
/*
void user_task(void)
{
(void)waiter3(NULL);
} */
int main(void)
{
int ret;
pthread_t thread1, thread2, thread3;
pthread_mutexattr_t attr;
if ((ret = pthread_mutexattr_init(&attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_mutex_init(&mutex, &attr)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_cond_init(&cv, NULL)) != 0) {
/* handle error */
fatal_error();
}
if ((ret = pthread_create(&thread1, NULL, &waiter1, NULL))) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_create(&thread2, NULL, &waiter2, NULL))) {
/* handle error */
fatal_error();
}
if ((ret = pthread_create(&thread3, NULL, &signaler, NULL))) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread1, NULL)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread2, NULL)) != 0) {
/* Handle error */
fatal_error();
}
if ((ret = pthread_join(thread3, NULL)) != 0) {
/* Handle error */
fatal_error();
}
while (1) { ; }
return 0;
}