Main Content

MISRA C:2012 Rule 22.19

A condition variable shall be associated with at most one mutex object

Since R2025b

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

Rule Definition

A condition variable shall be associated with at most one mutex object1 .

This rule comes from MISRA C™: 2012 Amendment 4.

Rationale

Condition variables are used for unlocking a mutex by a signal. If a condition variable is associated with more than one mutex object, it is undefined which mutex object is signaled by it. That is, when the conditional variable is signaled, it is unclear which thread is unlocked.

When using condition variables for synchronization, associate unique condition variables to each mutex object.

Polyspace Implementation

Polyspace reports a violation of this rule when multiple mutex objects concurrently wait on the same condition variable.

Polyspace supports the checking for violations of this rule when the code uses these concurrency libraries: POSIX®, C11, Visual C11, and WinAPI.

Troubleshooting

If you expect a rule violation but do not see it, refer to Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

In this example, different mutexes are used to protect the variables count1, count2, and count3. Since the three functions waiter1(), waiter2(), and each wait on the same condition variable cv with different mutexes, the call to pthread_cond_wait succeeds for one of the threads and the call is undefined for the other two.

Polyspace reports a violation for function waiter3() even though the function is not invoked directly or indirectly by a thread, entry-point, or interrupt. The analysis considers function waiter3() called by main() through its function address or by an unidentified thread.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define Thrd_return_ 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) { //Noncompliant
            /* 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) { //Noncompliant
            /* 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) { //Noncompliant
            /* 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;
}

In this example, mutexes myMutex1 and myMutex2 both concurrently wait on the condition variable myCond. When this variable is signaled in sync(), it is undefined which mutex is unlocked. Polyspace reports violations.

#include <stdint.h>
#include <threads.h>
#include <stdio.h>
#include <time.h>

mtx_t myMutex1;
mtx_t myMutex2;
cnd_t myCond;
int32_t foo(void) {
	mtx_lock(&myMutex1);
	cnd_wait(&myCond, &myMutex1); //Noncompliant
	mtx_unlock(&myMutex1);
	return 0;
}

int32_t bar(void) {
	mtx_lock(&myMutex2);
	cnd_wait(&myCond, &myMutex2); //Noncompliant
	mtx_unlock(&myMutex2);
	return 0;
}

int32_t sync(void){
    //...
    cnd_signal(&myCond);
    //..
}
int main(void) {
    // Initialize mutexes and condition variable
    mtx_init(&myMutex1, mtx_plain);
    mtx_init(&myMutex2, mtx_plain);
    cnd_init(&myCond);
    thrd_t thread1, thread2, thread3;
    
    // Create threads
    thrd_create(&thread1, foo, NULL);
    thrd_create(&thread2, bar, NULL);
    
    // Give time for threads foo and bar to wait
    thrd_sleep(&(struct timespec){.tv_sec = 1, .tv_nsec = 0}, NULL);
    
    // Create thread sync to signal the condition variable
    thrd_create(&thread3, sync, NULL);
    
    // Wait for threads to finish
    thrd_join(thread1, NULL);
    thrd_join(thread2, NULL);
    thrd_join(thread3, NULL);

    // Destroy mutexes and condition variable
    mtx_destroy(&myMutex1);
    mtx_destroy(&myMutex2);
    cnd_destroy(&myCond);
    return 0;

}

Check Information

Group: Resources
Category: Required
AGC Category: Required

Version History

Introduced in R2025b


1 All MISRA coding rules and directives are © Copyright The MISRA Consortium Limited 2021.

The MISRA coding standards referenced in the Polyspace Bug Finder™ documentation are from the following MISRA standards:

  • MISRA C:2004

  • MISRA C:2012

  • MISRA C:2023

  • MISRA C++:2008

  • MISRA C++:2023

MISRA and MISRA C are registered trademarks of The MISRA Consortium Limited 2021.