* Add utility class: ReadWriteMutex;

This class allows multiple readers to obtain a lock at the same time. Until a writer attempts to obtain a lock, then attempts by readers to lock will block. Also second writers that attempt to lock will block until the first releases its lock.

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@1644 4a71c877-e1ca-e34f-864e-861f7616d084
master
Giel van Schijndel 2007-05-15 20:57:49 +00:00
parent aaac0278f5
commit 831dbe0c1d
3 changed files with 250 additions and 1 deletions

View File

@ -22,7 +22,7 @@
MAKERULES=../../../makerules
include $(MAKERULES)/configure.mk
OBJS=main.o networking/tcp_server.o
OBJS=main.o read_write_mutex.o networking/tcp_server.o
BOOST_DIR=C:/boost_1_33_1
BOOST_ASIO_DIR=C:/boost_asio_0_3_7

View File

@ -0,0 +1,177 @@
/*
Warzone 2100 Lobbyserver, serves as a meeting place to set up games
Copyright (C) 2007 Giel van Schijndel
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
$Revision$
$Id$
$HeadURL$
*/
#include "read_write_mutex.hpp"
#include <boost/thread.hpp>
// An exception safe (not thread safe though) counter
class RAIICounter : boost::noncopyable
{
public:
RAIICounter() :
_count(0)
{
}
inline operator unsigned int() const
{
return _count;
}
class RAIICounted : boost::noncopyable
{
public:
inline RAIICounted(RAIICounter& counter) :
_counter(counter)
{
++(_counter._count);
}
inline ~RAIICounted()
{
--(_counter._count);
}
private:
RAIICounter& _counter;
};
private:
unsigned int _count;
};
class ReadWriteMutex::impl : boost::noncopyable
{
public:
impl() :
_readerCount(0),
_currentWriter(false)
{
}
inline void acquireReadLock() const
{
boost::mutex::scoped_lock lock(_mutex);
// require a while loop here, since when the writerFinished condition is notified
// we should not allow readers to lock if there is a writer waiting
// if there is a writer waiting, we continue waiting
// If there currently is a writer lock, or writers are pending to lock then wait
while(_pendingWriters != 0 || _currentWriter)
{
// Block here to prevent busy waiting, we'll be awakened as soon as a writer has finished
_writerFinished.wait(lock);
}
++_readerCount;
}
inline void releaseReadLock() const
{
boost::mutex::scoped_lock lock(_mutex);
--_readerCount;
if(_readerCount == 0)
{
// notify all pending writers that there currently are no more readers
_allReadersFinished.notify_all();
}
}
inline void acquireWriteLock()
{
boost::mutex::scoped_lock lock(_mutex);
// ensure subsequent readers block
RAIICounter::RAIICounted pendWriter(_pendingWriters);
// wait until all reader locks are released
if(_readerCount != 0)
{
// * Release our mutex lock
// * Block until we're notified there are no more readers holding a lock
// * Reacquire our mutex lock (or block until we do so)
_allReadersFinished.wait(lock);
}
// only become a writer when there currently is none
while(_currentWriter)
{
// block until the current writer releases its lock
_writerFinished.wait(lock);
}
// Yay! We're now the current writer, make sure others know we are
_currentWriter = true;
}
inline void releaseWriteLock()
{
boost::mutex::scoped_lock lock(_mutex);
_currentWriter = false;
_writerFinished.notify_all();
}
private:
mutable boost::mutex _mutex;
mutable unsigned int _readerCount;
mutable boost::condition _allReadersFinished;
RAIICounter _pendingWriters;
bool _currentWriter;
mutable boost::condition _writerFinished;
};
// Mutex's constructor and destructor; handle construction/destruction of our PIMPL (Private IMPLementation)
ReadWriteMutex::ReadWriteMutex() :
pimpl(new impl)
{
}
ReadWriteMutex::~ReadWriteMutex()
{
delete pimpl;
}
// Constructor and destructor for readonly lock
ReadWriteMutex::scoped_readonlylock::scoped_readonlylock(const ReadWriteMutex& mutex) : _mutex(mutex)
{
_mutex.pimpl->acquireReadLock();
}
ReadWriteMutex::scoped_readonlylock::~scoped_readonlylock()
{
_mutex.pimpl->releaseReadLock();
}
// Constructor and destructor for write lock
ReadWriteMutex::scoped_lock::scoped_lock(ReadWriteMutex& mutex) : _mutex(mutex)
{
_mutex.pimpl->acquireWriteLock();
}
ReadWriteMutex::scoped_lock::~scoped_lock()
{
_mutex.pimpl->releaseWriteLock();
}

View File

@ -0,0 +1,72 @@
/*
Warzone 2100 Lobbyserver, serves as a meeting place to set up games
Copyright (C) 2007 Giel van Schijndel
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
$Revision$
$Id$
$HeadURL$
*/
#ifndef _INCLUDE_READ_WRITE_MUTEX_HPP_
#define _INCLUDE_READ_WRITE_MUTEX_HPP_
#include <boost/utility.hpp>
// Special mutex class which allows multiple readers at the same time
// When a writer requests access, no simultaneous readers or writers are allowed
// This class favours writers over readers (i.e. as long as there are writers
// in the queue they will be allowed before any readers are)
class ReadWriteMutex : boost::noncopyable
{
public:
ReadWriteMutex();
~ReadWriteMutex();
// Mutex lock classes
// Read lock, can always be acquired as long as no writers are pending or locked the mutex
// This lock additionally doesn't require a non-const mutex to be constructed from
class scoped_readonlylock : boost::noncopyable
{
public:
scoped_readonlylock(const ReadWriteMutex& mutex);
~scoped_readonlylock();
private:
const ReadWriteMutex& _mutex;
};
// Write lock, name and behaviour is similar to boost's mutex::scoped_lock
// (which is the reason for the similar name)
// This lock requires a non-const mutex to be constructed from
class scoped_lock : boost::noncopyable
{
public:
scoped_lock(ReadWriteMutex& mutex);
~scoped_lock();
private:
ReadWriteMutex& _mutex;
};
private:
class impl;
impl* pimpl;
};
#endif // _INCLUDE_READ_WRITE_MUTEX_HPP_