Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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
bgColor#FFcccc
langc
#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
bgColor#ccccff
langc
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

...