Если последовательно развить RAII идею критической секции то, очевидно, мы сделаем что-то вроде:
class FastMutex
{
public:
FastMutex()
: cs()
{
InitializeCriticalSection(&cs);
}
~FastMutex()
{
DeleteCriticalSection(&cs);
}
void enter()
{
EnterCriticalSection(&cs);
}
void leave()
{
LeaveCriticalSection(&cs);
}
private:
CRITICAL_SECTION cs;
};
А теперь, допустим, захотим это использовать примерно так:
struct LockedPoint
{
struct Point
{
size_t x;
size_t y;
Point(size_t x_, size_t y_)
: x(x_), y(y_){}
};
Point point;
FastMutex mutex;
LockedPoint(const Point &point_)
: point(point_), mutex() {}
};
typedef std::vector<LockedPoint> lockedpoints_s;
lockedpoints_s points; // Пока оставим за скобками RWLock вектора
Используем обычным образом:
points.push_back(LockedPoint(LockedPoint::Point(1, 1)));
И ... имеем не проявляющийся до поры, до времени баг. Он заключается в том, что на каждое добавление объекта в вектор у нас будет бесполезно создаваться и уничтожаться критическая секция, поскольку push_back() производит копирование.
Очевидным решением этой проблемы является запрет копирования, с вытекающим отсюда размещением на куче и передаче в вектор указателей.
С не менее очевидными граблями из-за возможных утечек (решается shared_ptr) и непреодолимыми проблемами производительности вызванные частой аллокацией/деаллокацией мелких объектов (если имеет место быть), что опять же можно решить создав свой аллокатор (перегрузив оператор new()). Но как-то все загадочно начинает выглядеть :)
Однако, есть метод проще.
Он заключается в том, чтобы исключить захват/освобождение ресурса в конструкторе/деструкторе объекта и отдать это дело пользователю.
Примерно так:
class FastMutex
{
public:
FastMutex()
: cs()
{
}
~FastMutex()
{
}
void create()
{
InitializeCriticalSection(&cs);
}
void destroy()
{
DeleteCriticalSection(&cs);
}
void enter()
{
EnterCriticalSection(&cs);
}
void leave()
{
LeaveCriticalSection(&cs);
}
private:
CRITICAL_SECTION cs;
};
Используем примерно так:
struct LockedPoint
{
struct Point
{
...
};
Point point;
FastMutex mutex;
LockedPoint(const Point &point_)
: point(point_), mutex(), mtxCreated(false) {}
void createMtx()
{
mutex.create();
mtxCreated = true;
}
~LockedPoint()
{
if (mtxCreated)
mutex.destroy();
}
private:
bool mtxCreated;
};
points.push_back(LockedPoint(LockedPoint::Point(1, 1)));
points.back().createMtx();
Заметьте, никто не мешает нам и в данном подходе устроить автоматическое освобождение мьютекса.
Для случаев, когда копирование объекта мьютекса неактуально можно написать некопируемый враппер который и будет осуществлять RAII:
class OwnedFastMutex // Non copyable but owned implementatition
{
public:
OwnedFastMutex()
: fastMutex()
{
fastMutex.create();
}
~OwnedFastMutex()
{
fastMutex.destroy();
}
void enter()
{
fastMutex.enter();
}
void leave()
{
fastMutex.leave();
}
private:
FastMutex fastMutex;
OwnedFastMutex(const OwnedFastMutex&);
OwnedFastMutex& operator=(const OwnedFastMutex&);
};
А чтобы использовать оба варианта в ScopedLock не прибегая к динамическому полиморфизму напишем шаблонный:
template <typename fastmutex_t>
class ScopedLock
{
public:
ScopedLock(fastmutex_t *cs_)
: cs(*cs_)
{
cs.enter();
}
~ScopedLock()
{
cs.leave();
}
private:
fastmutex_t &cs;
static void *operator new(size_t);
static void operator delete(void *);
ScopedLock(const ScopedLock&);
ScopedLock& operator=(const ScopedLock&);
};
Пользуемся им например так:
class ClientManager
{
public:
ClientManager()
: mutex(), clients()
{
}
~ClientManager()
{
}
void AddClient(const int id)
{
if (FindClient(id) != clients.end())
{
ScopedLock<OwnedFastMutex> lock(&mutex);
clients.push_back(id);
}
}
Clients::iterator FindClient(const int id)
{
ScopedLock<OwnedFastMutex> lock(&mutex);
for (Clients::const_iterator it = clients.begin(); it != cleints.end(); ++it)
if (*it == id)
return it;
return clients.end();
}
void DelClient(const int id)
{
Clients::iterator f_it = FindClient(id);
if (f_it == clients.end())
return;
ScopedLock<OwnedFastMutex> lock(&mutex);
clients.erase(f_it);
}
private:
OwnedFastMutex mutex;
typedef std::vector<int> Clients;
Clients clients;
};