* 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-861f7616d084master
parent
aaac0278f5
commit
831dbe0c1d
|
@ -22,7 +22,7 @@
|
||||||
MAKERULES=../../../makerules
|
MAKERULES=../../../makerules
|
||||||
include $(MAKERULES)/configure.mk
|
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_DIR=C:/boost_1_33_1
|
||||||
BOOST_ASIO_DIR=C:/boost_asio_0_3_7
|
BOOST_ASIO_DIR=C:/boost_asio_0_3_7
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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_
|
Loading…
Reference in New Issue