Saturday, October 19, 2013

A ReadWrite implementation with preference to waiting writers.

Description:  Below is an implementation of ReadWrite using Mutex and Condition Variables with preference to waiting writers. Before a read lock is given, we check if there are any writers waiting to get write lock. Similary, when a lock is released, if a writer is waiting to get the lock , preference is given to them over readers.

RwLock.h (Header File)

#include <pthread.h>


class RwLock
{
    //nreaders = 0 , indicates no. reader/writer exists.
    //nreaders > 0, indicates one or more read lock exists.
    //nreaders=-1, indicates a write lock exists.

    int nreaders;

    int nwaitwriters;
   
    pthread_mutex_t rw_mutex;

    pthread_cond_t rw_condreader;
   
    pthread_cond_t rw_condwriter;

    public:

    RwLock();

    void getReadLock();

    void getWriteLock();

    void unLock();

    ~RwLock();

       
};


RwLock.C (Implementation)

#include "RwLock.h"

RwLock :: RwLock()
{
    nreaders = 0;

    nwaitwriters = 0;

    pthread_mutex_init(&rw_mutex, NULL);   

    pthread_cond_init(&rw_condreader, NULL);

    pthread_cond_init(&rw_condwriter, NULL);
}

void RwLock :: getReadLock()
{
    //Obtain Lock.
    pthread_mutex_lock(&rw_mutex);

    //If  a WRITER exists(nreaders = -1)wait for signal from writer.
    while(-1 == nreaders || 0 < nwaitwriters)
    {
        pthread_cond_wait(&rw_condwriter, &rw_mutex);
    }

    //increment no. of readers
    nreaders++;

    pthread_mutex_unlock(&rw_mutex);       
}


void RwLock :: getWriteLock()
{
    //Obtain Lock
    pthread_mutex_lock(&rw_mutex);

    //if nreader is != 0 it means either a reader or writer is present. wait for signal from reader/writer (do it in a loop)
    while(0 != nreaders)
    {
        //Increment no. of waiting writers.
        nwaitwriters++;

        pthread_cond_wait(&rw_condreader, &rw_mutex);
       
        //Decrement no. of waiting writers.
        nwaitwriters--;
       
    }

    //set nreader = -1 indicating a writer is present.
    nreaders = -1;

    //unlock the mutex.
    pthread_mutex_unlock(&rw_mutex);
}

void RwLock :: unLock()
{
    //Obtain Lock
    pthread_mutex_lock(&rw_mutex);

    if(0 < nreaders)
    {
        //one of the readers is done. decrement readers count.
        nreaders--;

        if(0 == nreaders)
        {
            //last reader is done.

            if(0 < nwaitwriters)
            {
                //if a writer is waiting
                pthread_cond_signal(&rw_condreader);
            }
        }
    }
    else if(-1 == nreaders)
    {
        //a writer is done. set nreaders to 0 indicating no reader/writer exists.
        nreaders = 0;

        if(0 < nwaitwriters)
        {
            //if a writer is waiting.
            pthread_cond_signal(&rw_condreader);
        }
        else
        {           
            //broadcast to any waiting readers.
            pthread_cond_broadcast(&rw_condwriter);
        }
    }

    pthread_mutex_unlock(&rw_mutex);
}

RwLock :: ~RwLock()
{
    //Destroy condition variable and mutex.
    pthread_mutex_destroy(&rw_mutex);

    pthread_cond_destroy(&rw_condreader);

    pthread_cond_destroy(&rw_condwriter);
}

Sunday, October 6, 2013

Implementation of readwrite lock using mutex and condition variables.

Description: Below is an implementation of readwrite lock using mutex and condition variables. Note that, the implementation does not give any preference to waiting writers.


RwLock.h (Header File)

#include <pthread.h>
 
class RwLock
{
    //nreaders = 0 , indicates no. reader/writer exists.
    //nreaders > 0, indicates one or more read lock exists.
    //nreaders=-1, indicates a write lock exists.

    int nreaders;
   
    pthread_mutex_t rw_mutex;

    pthread_cond_t rw_cond;

    public:

    RwLock();

    void getReadLock();

    void getWriteLock();

    void unLock();

    ~RwLock();
       
};

RwLock.C (Implementation of our  ReadWrite Lock)
#include "RwLock.h"

RwLock :: RwLock()
{
    nreaders = 0;
   
    // Initialize mutex and condition variable.
    pthread_mutex_init(&rw_mutex, NULL);   

    pthread_cond_init(&rw_cond, NULL);
}

void RwLock :: getReadLock()
{
    //Obtain Lock.
    pthread_mutex_lock(&rw_mutex);

    // If  a WRITER exists(nreaders = -1)wait for signal from writer.
    while(-1 == nreaders)
    {
        pthread_cond_wait(&rw_cond, &rw_mutex);
    }

    // Increment no. of reader
    nreaders++;

    //Unlock the mutex.
    pthread_mutex_unlock(&rw_mutex);       
}


void RwLock :: getWriteLock()
{
    //Obtain Lock
    pthread_mutex_lock(&rw_mutex);

    // If nreader is != 0 it means either a reader or writer is present. wait for signal from reader/writer (do it in a loop)
    while(0 != nreaders)
    {
        pthread_cond_wait(&rw_cond, &rw_mutex);
    }

    // Set nreader = -1 indicating a writer is present.
    nreaders = -1;

    // Unlock the mutex.
    pthread_mutex_unlock(&rw_mutex);
}

void RwLock :: unLock()
{
    // Obtain Lock.
    pthread_mutex_lock(&rw_mutex);

    if(0 < nreaders)
    {
        // One of the readers is done. decrement readers count.
        nreaders--;

        if(0 == nreaders)
        {
            // Last reader is done.
            pthread_cond_broadcast(&rw_cond);
        }
    }
    else if(-1 == nreaders)
    {
        // A writer is done. set nreaders to 0 indicating no reader/writer exists.
        nreaders = 0;
        pthread_cond_broadcast(&rw_cond);
    }
    // Unlock the mutex.
    pthread_mutex_unlock(&rw_mutex);
}

RwLock :: ~RwLock()
{
    // Destroy condition variable and mutex.
    pthread_mutex_destroy(&rw_mutex);

    pthread_cond_destroy(&rw_cond);
}

Below is a sample program that uses this implementation and output obtained.


#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<iostream>

#include "RwLock.h"

using namespace std;

RwLock rwlock;

void* reader(void* arg)
{
    cout << "Obtaining read lock." << endl;

    rwlock.getReadLock();
   
    cout << "Obtained read lock." << endl;

    sleep(5);

    cout << "Releasing read lock." << endl;

    rwlock.unLock();

    cout << "Released read lock." << endl;   
}


void* writer(void* arg)
{
    cout << "Obtaining write lock." << endl;

    rwlock.getWriteLock();
   
    cout << "Obtained write lock." << endl;

    sleep(4);

    cout << "Releasing write lock." << endl;

    rwlock.unLock();

    cout << "Released write lock." << endl;   
}

int main(int argc, char** argv)
{
    pthread_t r1,r2,r3,w1,w2;

    int res;

    cout << endl;   

    res = pthread_create(&r1, NULL, reader, NULL);

    if(0 != res)
    {
        cout << "Failed to start thread." << endl;
        exit(1);
    }
           
    res = pthread_create(&r2, NULL, reader, NULL);

    if(0 != res)
    {
        cout << "Failed to start thread." << endl;
        exit(1);
    }           

    sleep(1);

    res = pthread_create(&w1, NULL, writer, NULL);

    if(0 != res)
    {
        cout << "Failed to start thread." << endl;
        exit(1);
    }   

    res = pthread_create(&w2, NULL, writer, NULL);

    if(0 != res)
    {
        cout << "Failed to start thread." << endl;
        exit(1);
    }   
    pthread_exit(NULL);       
}

Output:
Obtaining read lock.
Obtained read lock. 
Obtaining read lock.
Obtained read lock.            /*Two read locks obtained*/
Obtaining write lock.
Obtaining write lock.         /*Two threads waiting for write lock*/
Releasing read lock.
Released read lock.
Releasing read lock.
Released read lock.         /*Two read locks are released*/
Obtained write lock.       
Releasing write lock.
Released write lock.       /*A thread first gets write lock and releases it*/  
Obtained write lock.      /*Second thread gets the write lock*/
Releasing write lock.
Released write lock.