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;
}