Main Content

MISRA C:2023 Rule 22.17

No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked before

Since R2024b

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

No thread shall unlock a mutex or call cnd_wait() or cnd_timedwait() for a mutex it has not locked before

Rationale

If a thread attempts to unlock a mutex that it did not lock previously, the resulting behavior is undefined. Consider this code:

mtx_t mutex;

void foo() {
	//...
	mtx_unlock(&mutex);
}
The thread foo() does not lock mutex but attempts to unlock it, resulting in undefined behavior.

If a thread attempts to call the functions cnd_wait() or cnd_timedwait() using a mutex without locking the mutex first, the resulting behavior is undefined. Consider this code:

mtx_t mutex;
cnd_t cnd1
void foo() {
	//...
	cnd_wait(&mutex, &cnd1);
}
The thread foo does not lock mutex but calls cnd_wait() using the mutex object, resulting in undefined behavior.

To avoid undefined behavior, a thread must lock a mutex before attempting either of these operations:

  • Unlocking the mutex

  • Calling cnd_wait() or cnd_timedwait() using the mutex

Polyspace Implementation

Polyspace reports a violation of this rule if either of these conditions is true:

  • A thread unlocks a mutex object before locking it.

  • A thread attempts to unlock a previously locked mutex twice without a locking operation between the unlocking operations.

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, the thread t2 containing the function worker2() attempts to unlock mutex but t2 does not lock the mutex object first, resulting in undefined behavior. Polyspace reports a violation.

#include <stdio.h>
#include <threads.h>
void doWork(void);
// Mutex declaration
mtx_t mutex;

// Worker function 1: Correctly locks and then unlocks the mutex
int worker1(void* arg) {
    printf("Worker 1 is trying to lock the mutex.\n");
    if (mtx_lock(&mutex) == thrd_success) {
        printf("Worker 1 has locked the mutex.\n");

        doWork();
        printf("Worker 1 is unlocking the mutex.\n");
        mtx_unlock(&mutex);
    } else {
        printf("Worker 1 failed to lock the mutex.\n");
    }
    return 0;
}

// Worker function 2: Tries to unlock the mutex without locking it first
int worker2(void* arg) {

    printf("Worker 2 is attempting to unlock the mutex without locking it first.\n");
    if (mtx_unlock(&mutex) == thrd_success) { /* Noncompliant */
        printf("Worker 2 successfully unlocked the mutex.\n");
        doWork();
    } else {
        printf("Worker 2 failed to unlock the mutex.\n");
    }
    return 0;
}


int main() {
	thrd_t t1, t2;

	// Initialize mutex
	mtx_init(&mutex, mtx_plain);

	// Create worker threads
	if(thrd_create(&t1, worker1, NULL) != thrd_success) {
		printf("Failed to create thread 1\n");
		return 1;
	}
	if(thrd_create(&t2, worker2, NULL) != thrd_success) {
		printf("Failed to create thread 2\n");
		return 1;
	}

	// Wait for threads to finish
	thrd_join(t1, NULL);
	thrd_join(t2, NULL);

	// Clean up
	mtx_destroy(&mutex);

	return 0;
}

For this example, specify worker1 and worker2 as entry-point functions. In the Polyspace User Interface, specify these options:

Alternatively, at the command line, use this command:

polyspace-bug-finder -entry-points worker1,worker2

In this example, the thread t1 containing the function worker1() attempts to unlock a mutex that is already unlocked, which results in undefined behavior. Polyspace reports a violation.

#include <stdio.h>
#include <threads.h>
void doWork(void);
// Mutex declaration
mtx_t mutex;

// Worker function 1: Locks the mutex and then attempts to unlock it twice
int worker1(void *arg) {
	printf("Worker 1 is trying to lock the mutex.\n");
	if(mtx_lock(&mutex) == thrd_success) {
		printf("Worker 1 has locked the mutex.\n");

		doWork();
		printf("Worker 1 is unlocking the mutex for the first time.\n");
		mtx_unlock(&mutex);

		printf("Worker 1 is attempting to unlock the mutex for the second time.\n");
		if(mtx_unlock(&mutex) == thrd_success) { /*Noncompliant*/
			printf("Worker 1 successfully unlocked the mutex a second time.\n");
		} else {
			printf("Worker 1 failed to unlock the mutex a second time. This is expected behavior.\n");
		}
	} else {
		printf("Worker 1 failed to lock the mutex.\n");
	}
	return 0;
}

// Worker function 2: Correctly locks and then unlocks the mutex
int worker2(void *arg) {
	printf("Worker 2 is trying to lock the mutex.\n");
	if(mtx_lock(&mutex) == thrd_success) {
		printf("Worker 2 has locked the mutex.\n");

		doWork();
		printf("Worker 2 is unlocking the mutex.\n");
		mtx_unlock(&mutex);
	} else {
		printf("Worker 2 failed to lock the mutex.\n");
	}
	return 0;
}


int main() {
	thrd_t t1, t2;

	// Initialize mutex
	mtx_init(&mutex, mtx_plain);

	// Create worker threads
	if(thrd_create(&t1, worker1, NULL) != thrd_success) {
		printf("Failed to create thread 1\n");
		return 1;
	}
	if(thrd_create(&t2, worker2, NULL) != thrd_success) {
		printf("Failed to create thread 2\n");
		return 1;
	}

	// Wait for threads to finish
	thrd_join(t1, NULL);
	thrd_join(t2, NULL);

	// Clean up
	mtx_destroy(&mutex);

	return 0;
}

For this example, specify worker1 and worker2 as entry-point functions. In the Polyspace User Interface, specify these options:

Alternatively, at the command line, use this command:

polyspace-bug-finder -entry-points worker1,worker2

Check Information

Group: Resources
Category: Required
AGC Category: Required

Version History

Introduced in R2024b