Demo Program - Mutex Locks in C

read_write_lock.h

/*********************************************************************
*
* Description:
*
* See source code file.
*
* History:
*  30 Jan 1999  TL Wolfe
*     - Original code developed
*
*********************************************************************/

#ifndef READ_WRITE_LOCK_H
#define READ_WRITE_LOCK_H

#include <pthread.h>

typedef struct
{
   pthread_mutex_t  mutex;         /* data structure access mutex */
   int              readsactive;   /* number of threads reading */
   int              readswaiting;  /* number of threads waiting to read */
   int              writesactive;  /* number of threads writting */
   int              writeswaiting; /* number of threads waiting to write */
   pthread_cond_t   readfree;      /* reads  conditional variable */
   pthread_cond_t   writefree;     /* writes conditional variable */
}  READ_WRITE_LOCK;

int   InitializeLock(READ_WRITE_LOCK*);
int   GetReadLock(READ_WRITE_LOCK*);
int   GetWriteLock(READ_WRITE_LOCK*);
int   ReleaseReadLock(READ_WRITE_LOCK*);
int   ReleaseWriteLock(READ_WRITE_LOCK*);
void  DisplayLockData(READ_WRITE_LOCK*,char*);

#endif

read_write_lock.c

/*********************************************************************
*
* Description:
*
* These routines provide synchronization to a resource by multiple
* read and write threads.  The lock has the following characteristics:
*
*   - Read and write threads have two states:
*       active  - accessing a resource.
*       waiting - waiting to access a resource.
*   - Multiple reads may be active at the same time.
*   - Only one write may be active at a time.
*   - Writes (active and waiting) have priority over waiting reads.
*   - Writes wait until all active reads are finished before
*     one of them become active.
*   - If no writes are active or waiting, reads become
*     active immediately otherwise they wait until all of the
*     writes (active and waiting) have completed.
*   - When more that one write is waiting, the one selected for
*     activation is randomly chosen.
*
* The model implemented above may be described as "write seldom,
* read often".  If this model does not fit your data access
* characteristics, the lock implemented here will give writes
* unfair access to the resource.
*
* History:
*  30 Jan 1999  TL Wolfe
*     - Original code developed
*
*********************************************************************/

/* include files */

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#include <read_write_lock.h>


/* global variables */

extern int Debug;


/*---------------------------------------------------------------------*/
/* Initialize a read/write lock data structure.                        */
/* (this should be done only one time per structure.)                  */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/*                                                                     */
/*---------------------------------------------------------------------*/

int InitializeLock (READ_WRITE_LOCK *Lock)

{
   if (Lock == NULL)
   {
      printf("InitializeLock: lock pointer is null\n");
      exit(1);
   }

   Lock->readsactive   = 0;
   Lock->readswaiting  = 0;
   Lock->writesactive  = 0;
   Lock->writeswaiting = 0;

   if (pthread_mutex_init(&(Lock->mutex),NULL) != 0)
   {
      perror("InitializeLock:pthread_mutex_init");
      exit(1);
   }

   if (pthread_cond_init(&(Lock->readfree),NULL) != 0)
   {
      perror("InitializeLock:pthread_cond_init");
      exit(1);
   }


   if (pthread_cond_init(&(Lock->writefree),NULL) != 0)
   {
      perror("InitializeLock:pthread_cond_init");
      exit(1);
   }

   if (Debug > 1) DisplayLockData(Lock,"initialize lock");

   return 1;
}


/*---------------------------------------------------------------------*/
/* Get a read lock.  When this routine returns read access is granted  */
/* until the lock is released.                                         */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/*                                                                     */
/*---------------------------------------------------------------------*/

int GetReadLock (READ_WRITE_LOCK *Lock)

{
   if (Lock == NULL)
   {
      printf("GetReadLock: lock pointer is null\n");
      exit(1);
   }

   if (pthread_mutex_lock(&(Lock->mutex)) != 0)
   {
      perror("GetReadLock:pthread_mutex_lock");
      exit(1);
   }

   Lock->readswaiting++;

   while ((Lock->writeswaiting > 0) || (Lock->writesactive > 0))
   {
      if (Debug > 1) DisplayLockData(Lock,"waiting for read lock");

      if (pthread_cond_wait(&(Lock->readfree),&(Lock->mutex)) != 0)
      {
         perror("GetReadLock:pthread_cond_wait");
         exit(1);
      }
   }

   Lock->readsactive++;

   Lock->readswaiting--;

   if (Lock->readswaiting < 0)
   {
      printf("GetReadLock: reads waiting count less than zero\n");
      exit(1);
   }

   if (Debug > 1) DisplayLockData(Lock,"got read lock");

   if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
   {
      perror("GetReadLock:pthread_mutex_unlock");
      exit(1);
   }

   return 1;
}


/*---------------------------------------------------------------------*/
/* Get a write lock.  When this routine returns write access has been  */
/* granted until the lock is released.                                 */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/*                                                                     */
/*---------------------------------------------------------------------*/

int GetWriteLock (READ_WRITE_LOCK *Lock)

{
   if (Lock == NULL)
   {
      printf("GetWriteLock: lock pointer is null\n");
      exit(1);
   }

   if (pthread_mutex_lock(&(Lock->mutex)) != 0)
   {
      perror("GetWriteLock:pthread_mutex_lock");
      exit(1);
   }

   Lock->writeswaiting++;

   while ((Lock->writesactive > 0) || (Lock->readsactive > 0))
   {
      if (Debug > 1) DisplayLockData(Lock,"waiting for write lock");

      if (pthread_cond_wait(&(Lock->writefree),&(Lock->mutex)) != 0)
      {
         perror("GetWriteLock:pthread_cond_wait");
         exit(1);
      }
   }

   Lock->writesactive = 1;

   Lock->writeswaiting--;

   if (Lock->writeswaiting < 0)
   {
      printf("GetWriteLock: writes waiting count less than zero\n");
      exit(1);
   }

   if (Debug > 1) DisplayLockData(Lock,"got write lock");

   if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
   {
      perror("GetWriteLock:pthread_mutex_unlock");
      exit(1);
   }

   return 1;
}


/*---------------------------------------------------------------------*/
/* Release a read lock.                                                */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/*                                                                     */
/*---------------------------------------------------------------------*/

int ReleaseReadLock (READ_WRITE_LOCK *Lock)

{
   if (Lock == NULL)
   {
      printf("ReleaseReadLock: lock pointer is null\n");
      exit(1);
   }

   if (pthread_mutex_lock(&(Lock->mutex)) != 0)
   {
      perror("ReleaseReadLock:pthread_mutex_lock");
      exit(1);
   }

   if (Lock->readsactive == 0)
   {
      if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
      {
         perror("ReleaseReadLock:pthread_mutex_unlock");
         exit(1);
      }

      return 0;
   }

   Lock->readsactive--;

   if (Lock->readsactive < 0)
   {
      printf("ReleaseReadLock: read count less than zero\n");
      exit(1);
   }

   if (Lock->writeswaiting > 0)
   {
      if (pthread_cond_signal(&(Lock->writefree)) != 0)
      {
         perror("ReleaseReadLock:pthread_cond_signal");
         exit(1);
      }
   }
   else if (Lock->readswaiting > 0)
   {
      if (pthread_cond_broadcast(&(Lock->readfree)) != 0)
      {
         perror("ReleaseReadLock:pthread_cond_broadcast");
         exit(1);
      }
   }

   if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
   {
      perror("ReleaseReadLock:pthread_mutex_unlock");
      exit(1);
   }

   if (Debug > 1) DisplayLockData(Lock,"released read lock");

   return 1;
}


/*---------------------------------------------------------------------*/
/* Release a write lock.                                               */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/*                                                                     */
/*---------------------------------------------------------------------*/

int ReleaseWriteLock (READ_WRITE_LOCK *Lock)

{
   if (Lock == NULL)
   {
      printf("ReleaseWriteLock: lock pointer is null\n");
      exit(1);
   }

   if (pthread_mutex_lock(&(Lock->mutex)) != 0)
   {
      perror("ReleaseWriteLock:pthread_mutex_lock");
      exit(1);
   }

   if (Lock->writesactive == 0)
   {
      if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
      {
         perror("ReleaseWriteLock:pthread_mutex_unlock");
         exit(1);
      }

      return 0;
   }

   Lock->writesactive = 0;

   if (Lock->writeswaiting == 0)
   {
      if (pthread_cond_broadcast(&(Lock->readfree)) != 0)
      {
         perror("ReleaseWriteLock:pthread_cond_signal");
         exit(1);
      }
   }
   else
   {
      if (pthread_cond_signal(&(Lock->writefree)) != 0)
      {
         perror("ReleaseWriteLock:pthread_cond_signal");
         exit(1);
      }
   }

   if (pthread_mutex_unlock(&(Lock->mutex)) != 0)
   {
      perror("ReleaseWriteLock:pthread_mutex_unlock");
      exit(1);
   }

   if (Debug > 1) DisplayLockData(Lock,"released write lock");

   return 1;
}


/*---------------------------------------------------------------------*/
/* Display lock data structure data.  This routine assumes that the    */
/* lock data structure has been locked with a mutex and will not       */
/* be modified while this routine is executing.                        */
/*                                                                     */
/* parameter    definition                                             */
/*                                                                     */
/* Lock         Pointer to read/write lock data structure              */
/* Description  Description string                                     */
/*                                                                     */
/*---------------------------------------------------------------------*/

void DisplayLockData (READ_WRITE_LOCK *Lock, char *Description)

{
   pthread_t tid;

   /* get the thread ID */

   tid = pthread_self();

   /* display data */

   printf("\n");
   printf("--Lock-------------------------\n");

   if (Description != NULL) printf("  %s\n",Description);

   printf("  thread ID      = %d\n",(int) tid);

   if (Lock == NULL)
   {
      printf("  Lock pointer is NULL\n");
   }
   else
   {
      printf("  reads  active  = %d\n",Lock->readsactive);
      printf("  reads  waiting = %d\n",Lock->readswaiting);
      printf("  writes active  = %d\n",Lock->writesactive);
      printf("  writes waiting = %d\n",Lock->writeswaiting);
   }

   printf("-------------------------------\n");
   printf("\n");

   fflush(stdout);

   return;
}

test_read_write_lock.c

#include <errno.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>

#include <read_write_lock.h>

/* global variables */

int                    Debug = 0;

static READ_WRITE_LOCK Lock;

/* subroutine prototypes */

static void   create_thread( void*(*)(void*),void*);
static void  *read_thread(void*);
static void  *write_thread(void*);

/*---------------------------------------------------------------------*/
/*                                                                     */
/*---------------------------------------------------------------------*/

main (int Argc, char *Argv[])

{

   /* initialize lock */

   InitializeLock(&Lock);

   /* process user commands */

   for(;;)
   {
      switch(getchar())
      {
         case '0':
	 {
            Debug = 0;
            break;
         }
         case '1':
	 {
            Debug = 1;
            break;
         }
         case '2':
	 {
            Debug = 2;
            break;
         }
         case 'd':
         case 'D':
	 {
            if (Debug) Debug = 0 ; else Debug = 2;
            break;
         }
         case 'e':
         case 'E':
	 {
            exit(0);
            break;
         }
         case 'h':
         case 'H':
	 {
            printf("\n");
            printf("------------HELP----------------\n");
            printf(" e  - exit program\n");
            printf(" d  - toggle debug flag\n");
            printf(" h  - display help information\n");
            printf(" l  - display lock data\n");
            printf(" r  - start read thread\n");
            printf(" w  - start write thread\n");
            printf("--------------------------------\n");
            printf("\n");
            break;
         }
         case 'l':
         case 'L':
	 {
            DisplayLockData(&Lock,"user display lock");
            break;
         }
         case 'r':
         case 'R':
	 {
            create_thread(read_thread,NULL);
            break;
         }
         case 'w':
         case 'W':
	 {
            create_thread(write_thread,NULL);
            break;
         }
      }
   }
}

/*---------------------------------------------------------------------*/
/*                                                                     */
/*---------------------------------------------------------------------*/

static void* read_thread (void *Arg)
{
   pthread_t tid;

   tid = pthread_self();

   printf("Start Read  Thread (%d)\n",tid);

   GetReadLock(&Lock);

   sleep(5);

   ReleaseReadLock(&Lock);

   printf("End   Read  Thread (%d)\n",tid);
}

/*---------------------------------------------------------------------*/
/*                                                                     */
/*---------------------------------------------------------------------*/

static void* write_thread (void *Arg)
{
   pthread_t tid;

   tid = pthread_self();

   printf("Start Write Thread (%d)\n",tid);

   GetWriteLock(&Lock);

   sleep(5);

   ReleaseWriteLock(&Lock);

   printf("End   Write Thread (%d)\n",tid);
}

/*---------------------------------------------------------------------*/
/*                                                                     */
/*---------------------------------------------------------------------*/

static void create_thread (void *(*Sub)(void*),void *Arg)

{
   pthread_t tid;

   if (pthread_create(
	      &tid,                     /* returned thread ID */
              NULL,                     /* thread attributes */
              *Sub,                     /* subroutine */
              Arg) != 0)                /* subroutine arguments */
   {
      perror("pthread_create");
      exit(1);
   }

   return;
}