The source files for this module are listed below. You can also download the module C_Lock as a zip archive; this archive contains all the source files and documentation.
The Lock module contains three classes that operate together to form a multithreading resource or code locking mechanism. Use these to create thread-safe data or code access.
CLock and CLockController are two classes that work together; the template CLockedResource is a special-purpose combination of these two classes.
CLock represents a lock that can be on or off for your thread. Use the lock to lock out other threads to parts of code or resources when you need exclusive use of them. CLock will manage a critical section in it's internals; all initialization and destruction is handled for you. The class operates through it's two methods Lock and Unlock; make sure you call an Unlock for every Lock you perform (or use the CLockController class).
CLockController objects allow you to operate your CLock's. In stead of manually calling CLock.Lock and hoping you don't forget to call Unlock afterwards (due to an exception or just bad luck), CLockController manages these things for you (using RAII). Use it's Lock method to make it lock another CLock. You may call Lock multiple times, with more than one CLock object in turn; this will lock all these locks. Calling Unlock releases all locked locks; if the CLockController object goes out of scope, it's destructor will also unlock all locked locks.
In case you want to be informed when a lock is locked/unlocked, you can derive your class from the ILockCallback event structure. When the lock is locked or unlocked, the OnLocked / OnUnlocked events will be fired, also telling you if the lock was or is now free.
The last class, CLockedResource, can be used when you want safe access to just a single resource. Specify the type of the resource as the template parameter. After this, you have a variable that will perform a Lock/Unlock on every get and set of that variable.
Each file belonging to this source code module is listed below.
/*******************************************************************************
Version: 7
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#ifndef INCLUDE_TWOLOGS_COMMON_LOCK_H
#define INCLUDE_TWOLOGS_COMMON_LOCK_H
#include <set>
#include <windows.h>
// Predefines
class CLock;
// Event callback interface for CLock
struct ILockCallback {
// Raised when the given lock is locked
virtual void OnLocked(CLock* poLock, bool bFirstLock);
// Raised when the given lock is unlocked
virtual void OnUnlocked(CLock* poLock, bool bLastLock);
};
class CLock {
public:
// Con- & destructor
CLock(ILockCallback* poCallback = nullptr);
virtual ~CLock();
// Tries to lock the lock
void Lock();
// Tries to unlock the lock
void Unlock();
// The number of times the lock is locked
long LockCount();
private:
// The event callback to use, if any
ILockCallback* m_poCallback;
// The critical section controlling the lock
CRITICAL_SECTION m_uLock;
// The lock count
volatile long m_nLockCount;
// Prevent copy contruction and assignment
CLock(const CLock& oOtherLock);
const CLock& operator =(const CLock& oOtherLock);
};
class CLockController {
public:
// Con- & destructor
CLockController();
CLockController(CLock& oLock);
CLockController(const CLockController& oOtherController);
virtual ~CLockController();
// Also locks the given lock
void Lock(CLock& oLock);
// Prematurely unlocks the lock(s)
void Unlock();
// Copying
CLockController& operator = (const CLockController& oOtherController);
private:
// The locks to control
typedef std::multiset<CLock*> CLockSet;
CLockSet m_apoLocks;
};
template<class Type>
class CLockedResource : public CLock {
public:
// Constructs the resource
CLockedResource(const CLockedResource<Type>& oOther) {
m_uResource = oOther.Get();
}
CLockedResource(const Type& uValue) {
m_uResource = uValue;
}
CLockedResource() {
}
// Gets the resource
operator Type() {
return Get();
}
// Sets the resource
const Type operator = (const Type& uNewValue) {
Set(uNewValue);
return uNewValue;
}
const Type operator = (const CLockedResource& oOther) {
return *this = oOther.Get();
}
// Compares the resource
bool operator == (const Type& uOther) {
return Get() == uOther;
}
bool operator == (const CLockedResource& oOther) {
if (this == &oOther)
return true;
else
return Get() == oOther.Get();
}
bool operator != (const Type& uOther) {
return Get() != uOther;
}
bool operator != (const CLockedResource& oOther) {
if (this == &oOther)
return false;
else
return Get() != oOther.Get();
}
protected:
// Gets the resource
const Type Get() {
// Lock the lock
CLockController oLock;
oLock.Lock(*this);
// And return the resource
volatile Type uResult = m_uResource;
return uResult;
}
// Sets the resource
void Set(const Type& uNewValue) {
// Lock the lock
CLockController oLock;
oLock.Lock(*this);
// And set the resource
m_uResource = uNewValue;
}
private:
// The resource to control
volatile Type m_uResource;
};
#endif // INCLUDE_TWOLOGS_COMMON_LOCK_H
/*******************************************************************************
Version: 7
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#include "Lock.h"
// Raised when the given lock is locked
void ILockCallback::OnLocked(CLock* poLock, bool bFirstLock) {
}
// Raised when the given lock is unlocked
void ILockCallback::OnUnlocked(CLock* poLock, bool bLastLock) {
}
// Constructor
CLock::CLock(ILockCallback* poCallback):
m_poCallback(poCallback),
m_nLockCount(0) {
InitializeCriticalSection(&m_uLock);
}
// Destructor
CLock::~CLock() {
DeleteCriticalSection(&m_uLock);
}
// Tries to lock the lock
void CLock::Lock() {
EnterCriticalSection(&m_uLock);
++m_nLockCount;
if (m_poCallback != nullptr) {
m_poCallback->OnLocked(this, m_nLockCount == 1);
}
}
// Tries to unlock the lock
void CLock::Unlock() {
--m_nLockCount;
if (m_poCallback != nullptr) {
m_poCallback->OnUnlocked(this, m_nLockCount == 0);
}
LeaveCriticalSection(&m_uLock);
}
// The number of times the lock is locked
long CLock::LockCount() {
return InterlockedCompareExchange(&m_nLockCount, 0, 0);
}
// Constructor
CLockController::CLockController() {
}
CLockController::CLockController(CLock& oLock) {
Lock(oLock);
}
CLockController::CLockController(const CLockController& oOtherController) {
m_apoLocks = oOtherController.m_apoLocks;
for (CLock* poNextLock: m_apoLocks) {
poNextLock->Lock();
}
}
// Destructor
CLockController::~CLockController() {
Unlock();
}
// Also locks the given lock
void CLockController::Lock(CLock& oLock) {
oLock.Lock();
m_apoLocks.insert(&oLock);
}
// Prematurely unlocks the lock(s)
void CLockController::Unlock() {
for (CLock* poNextLock: m_apoLocks) {
poNextLock->Unlock();
}
m_apoLocks.clear();
}
// Copying
CLockController& CLockController::operator = (const CLockController& oOtherController) {
if (this != &oOtherController) {
Unlock();
m_apoLocks = oOtherController.m_apoLocks;
for (CLock* poNextLock: m_apoLocks) {
poNextLock->Lock();
}
}
return *this;
}