Read/Write Lock in C

Table Of Contents

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_mute_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_mute_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_mute_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_mute_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_mute_lock"); exit(1); } if (Lock->readsactive == 0) { if (pthread_mutex_unlock(&(Lock->mutex)) != 0) { perror("ReleaseReadLock:pthread_mute_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_mute_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_mute_lock"); exit(1); } if (Lock->writesactive == 0) { if (pthread_mutex_unlock(&(Lock->mutex)) != 0) { perror("ReleaseWriteLock:pthread_mute_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_mute_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; }

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

test_read_write_lock.c

/************************************************************************ * * Description: * * Test Read Write Locks * * History: * 30 Jan 1999 TL Wolfe * - Original code developed * ************************************************************************/ #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 */ /*---------------------------------------------------------------------*/ 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; } } } } /*---------------------------------------------------------------------*/ /* read thread */ /*---------------------------------------------------------------------*/ 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); } /*---------------------------------------------------------------------*/ /* write thread */ /*---------------------------------------------------------------------*/ 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); } /*---------------------------------------------------------------------*/ /* create thread */ /*---------------------------------------------------------------------*/ 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; }