...
In this noncompliant code example, mutex1
protects count1
and mutex2
protects count2
. A race condition exists between the waiter1
and waiter2
threads because they use the same condition variable with different mutexes. If both threads attempt to call pthread_cond_wait()
at the same time, one thread will succeed and the other thread will invoke undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> #include <pthread.h> #include <assert.h> #include <unistd.h> #include <errno.h> pthread_mutex_t mutex1; pthread_mutex_t mutex2; pthread_mutexattr_t attr; pthread_cond_t cv; void *waiter1(); void *waiter2(); void *signaler(); int count1 = 0, count2 = 0; #define COUNT_LIMIT 5 int main() { int ret; pthread_t thread1, thread2, thread3; if ((ret = pthread_mutexattr_init( &attr)) != 0) { /* handleHandle error */ } if ((ret = pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) { /* handleHandle error */ } if ((ret = pthread_mutex_init( &mutex1, &attr)) != 0) { /* handleHandle error */ } if ((ret = pthread_mutex_init( &mutex2, &attr)) != 0) { /* handleHandle error */ } if ((ret = pthread_cond_init( &cv, NULL)) != 0) { /* handle error */ } if ((ret = pthread_create( &thread1, NULL, &waiter1, NULL))) { /* handleHandle error */ } if ((ret = pthread_create( &thread2, NULL, &waiter2, NULL))) { /* handle error */ } if ((ret = pthread_create( &thread3, NULL, &signaler, NULL))) { /* handleHandle error */ } if ((ret = pthread_join( thread1, NULL)) != 0) { /* handleHandle error */ } if ((ret = pthread_join( thread2, NULL)) != 0) { /* handleHandle error */ } if ((ret = pthread_join( thread3, NULL)) != 0) { /* handleHandle error */ } return 0; } void *waiter1() { int ret; while (count1 < COUNT_LIMIT) { if ((ret = pthread_mutex_lock(&mutex1)) != 0) { /* handleHandle error */ } if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) { /* handleHandle error */ } printf("count1 = %d\n", ++count1); if ((ret = pthread_mutex_unlock(&mutex1)) != 0) { /* handleHandle error */ } } return NULL; } void *waiter2() { int ret; while (count2 < COUNT_LIMIT) { if ((ret = pthread_mutex_lock(&mutex2)) != 0) { /* handleHandle error */ } if ((ret = pthread_cond_wait(&cv, &mutex2)) != 0) { /* handleHandle error */ } printf("count2 = %d\n", ++count2); if ((ret = pthread_mutex_unlock(&mutex2)) != 0) { /* handleHandle error */ } } return NULL; } void *signaler() { int ret; while ((count1 < COUNT_LIMIT) || (count2 < COUNT_LIMIT)) { sleep(1); printf("signaling\n"); if ((ret = pthread_cond_signal(&cv)) != 0) { /* handleHandle error */ } } return NULL; } |
...
pthread_cond_wait()
returns EINVAL
if it is called when another thread is waiting on the condition variable with a different mutex. This approach is arguably better because it forces the coder to fix the problem instead of allowing reliance on undefined behavior.
...
This problem can be solved either by always using the same mutex whenever a particular condition variable is used or by using separate condition variables, depending on how the code is expected to work. Here we use This compliant code uses the same-mutex solution:
Code Block | ||||
---|---|---|---|---|
| ||||
pthread_mutex_t mutex1; /* initializedInitialized as PTHREAD_MUTEX_ERRORCHECK */ pthread_cond_t cv; int count1 = 0, count2 = 0; void *waiter1() { int ret; while (count1 < COUNT_LIMIT) { if ((ret = pthread_mutex_lock(&mutex1)) != 0) { /* handleHandle error */ } if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) { /* handleHandle error */ } printf("count1 = %d\n", ++count1); if ((ret = pthread_mutex_unlock(&mutex1)) != 0) { /* handleHandle error */ } } return NULL; } void *waiter2() { int ret; while (count2 < COUNT_LIMIT) { if ((ret = pthread_mutex_lock(&mutex1)) != 0) { /* handleHandle error */ } if ((ret = pthread_cond_wait(&cv, &mutex1)) != 0) { /* handleHandle error */ } printf("count2 = %d\n", ++count2); if ((ret = pthread_mutex_unlock(&mutex1)) != 0) { /* handleHandle error */ } } return NULL; } |
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
POS53-C | mediumMedium | probableProbable | highHigh | P4 | L3 |
Bibliography
...