/*
 * Copyright (C) 2007-2014 Xagasoft, All rights reserved.
 *
 * This file is part of the libbu++ library and is released under the
 * terms of the license contained in the file LICENSE.
 */
#include "bu/readwritemutex.h"

Bu::ReadWriteMutex::ReadWriteMutex() :
    iCounter( 0 ),
    bWantWrite( false )
{
}

Bu::ReadWriteMutex::~ReadWriteMutex()
{
}

void Bu::ReadWriteMutex::lockRead()
{
    // Check to see if someone wants to write
    cWrite.lock();
    if( bWantWrite )
    {
        // If so, wait patiently for them to finish
        cWrite.wait();
    }
    cWrite.unlock();

    //  Now lock the read counter
    mRead.lock();
    iCounter++;
    // If the lock counter is one, we just locked for the first time,
    // so we lock the writer.
    if( iCounter == 1 )
        mWrite.lock();
    mRead.unlock();
}

void Bu::ReadWriteMutex::unlockRead()
{
    // Lock the read counter
    mRead.lock();
    iCounter--;
    // If we just decremented the counter back to zero then we can
    // release the write lock
    if( iCounter == 0 )
        mWrite.unlock();
    mRead.unlock();
}

//
// The bWantWrite could be a counter like the read lock counter, however
// once a write lock occurs and bWantWrite is set at least one write
// will definately occur.  In practice most writes all happen one after
// the other anyway and this way reads get a chance to mingle in.
//
// Really, just getting all current reads to stop so a write can happen
// I think is sufficient right now.
//
void Bu::ReadWriteMutex::lockWrite()
{
    // Lock the read counter
    mRead.lock();
    if( iCounter > 0 )
    {
        // If there is an active read in progress then we set the bWantWrite
        // flag to make sure no more readers start working.
        cWrite.lock();
        bWantWrite = true;
        cWrite.unlock();
    }
    mRead.unlock();

    // Lock the write lock
    mWrite.lock();
}

void Bu::ReadWriteMutex::unlockWrite()
{
    // Just always set the bWantWrite flag to false at this point, as long
    // as we're locked.
    cWrite.lock();
    bWantWrite = false;
    cWrite.unlock();

    // Release all waiting readers, they won't actually do much until we
    // unlock the write lock though
    cWrite.broadcast();

    // Unlock the write lock
    mWrite.unlock();
}