* 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
|
||||
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
|
||||
|
|
|
@ -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