JMU
Ensuring the Mutual Exclusion of Shared Variables
in Pthreads


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
The Good and the Bad
The Bad (cont.)
unixexamples/mutexes/threaded_counter_race.c
        #include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

static volatile long global_counter = 0;


static void*
counter(void *arg)
{
  long   i, limit, local_counter;

  limit = *((long *) arg);
  for (i=0; i<limit; i++)
    {
      local_counter = global_counter;
      ++local_counter;
      global_counter = local_counter;

      // Try replacing the three statements above with the following
      //++global_counter;
    }
  
  return NULL;
}



int
main(int argc, char *argv[])
{
  long        limit;
  pthread_t  thread_a, thread_b;
  
  if (argc <= 1) limit = 100;
  else           limit = atol(argv[1]);

  // Start two threads counting
  pthread_create(&thread_a, NULL, counter, &limit);
  pthread_create(&thread_b, NULL, counter, &limit);

  // Wait for both threads to terminate
  pthread_join(thread_a, NULL);
  pthread_join(thread_b, NULL);

  // Display the result
  printf("Expected: %ld\n", limit*2);
  printf("Actual:   %ld\n", global_counter);
  exit(0);
}
        
The Bad (cont.)
The Bad (cont.)
The Bad (cont.)
Coordination
Ensuring Mutual Exclusion
Using "Mutexes" (i.e., pthread_mutex_t Variables)
Using "Mutexes" (cont.)
Creating Mutexes
Creating Mutexes (cont.)
State Transitions
Locking: pthread_mutex_lock() pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex)
Purpose:
Lock (i.e., acquire or become the owner of) a mutex
Details:
mutex The mutex
Return 0 on success; positive error number on error
#include <pthread.h> pthread.h
Attempting to Lock a Locked Mutex
Unlocking: pthread_mutex_unlock() pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex)
Purpose:
Unlock (i.e., release) a mutex
Details:
mutex The mutex
Return 0 on success; positive error number on error
#include <pthread.h> pthread.h
Unlock Errors
A Corrected Example
unixexamples/mutexes/threaded_counter_mutex.c
        #include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

static volatile long     global_counter = 0;
static pthread_mutex_t  counter_lock = PTHREAD_MUTEX_INITIALIZER;

static void*
counter(void *arg)
{
  long   i, limit, local_counter;

  limit = *((long *) arg);
  for (i=0; i<limit; i++)
    {
      pthread_mutex_lock(&counter_lock);
      
      // Start of Critical Section
      local_counter = global_counter;
      ++local_counter;
      global_counter = local_counter;
      // End of Critical Section

      pthread_mutex_unlock(&counter_lock);
    }
  
  return NULL;
}



int
main(int argc, char *argv[])
{
  long       limit;
  pthread_t  thread_a, thread_b;
  
  if (argc <= 1) limit = 100;
  else           limit = atol(argv[1]);

  // Start two threads counting
  pthread_create(&thread_a, NULL, counter, &limit);
  pthread_create(&thread_b, NULL, counter, &limit);

  // Wait for both threads to terminate
  pthread_join(thread_a, NULL);
  pthread_join(thread_b, NULL);

  // Display the result
  printf("Expected: %ld\n", limit*2);
  printf("Actual:   %ld\n", global_counter);
  exit(0);
}
        
Performance
Performance (cont.)
Variants of pthread_mutex_lock()
Avoiding Deadlocks
Dynamic Initialization: pthread_mutex_init() pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
Purpose:
Initialize a mutex
Details:
mutex The mutex to be initialized
attr The attributes to use (of NULL for the default attributes)
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h
Mutex Attributes
Destruction
Destruction (cont.): pthread_mutex_destroy() pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex)
Purpose:
Indicate that a mutex will no longer be used
Details:
mutex The mutex to be destroyed
Return 0 on success; a positive error number on error
#include <pthread.h> pthread.h