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.
TLock and TLocker are two classes that work together; the template TLockedValue is a special-purpose combination of these two classes.
TLock 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. TLock 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 TLocker class).
TLocker objects allow you to operate your TLock's. In stead of manually calling TLock.Lock and hoping you don't forget to call Unlock afterwards (due to an exception or just bad luck), TLocker manages these things for you (using RAII). Use it's Lock method to make it lock another TLock. You may call Lock multiple times, with more than one TLock object in turn; this will lock all these locks. Calling Unlock releases all locked locks; if the TLocker 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, TLockedValue, can be used when you want safe access to just a single value. Specify the type of the value 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: 8
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 TLock;
// Event callback interface for TLock
struct ILockCallback {
// Raised when the given lock is locked
virtual void OnLocked(TLock* lockPtr, bool isFirstLock);
// Raised when the given lock is unlocked
virtual void OnUnlocked(TLock* lockPtr, bool isLastLock);
};
class TLock {
public:
// Con- & destructor
TLock(ILockCallback* callbackPtr = nullptr);
virtual ~TLock();
// 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_callbackPtr;
// The critical section controlling the lock
CRITICAL_SECTION m_lock;
// The lock count
volatile long m_lockCount;
// Prevent copy contruction and assignment
TLock(const TLock& otherLock);
const TLock& operator =(const TLock& otherLock);
};
class TLocker {
public:
// Con- & destructor
TLocker();
TLocker(TLock& lock);
TLocker(const TLocker& otherLocker);
virtual ~TLocker();
// Also locks the given lock
void Lock(TLock& lock);
// Prematurely unlocks the lock(s)
void Unlock();
// Copying
TLocker& operator = (const TLocker& otherLocker);
private:
// The locks to control
typedef std::multiset<TLock*> TLockPtrs;
TLockPtrs m_lockPtrs;
};
template<class TType>
class TLockedValue: public TLock {
public:
// Constructs the resource
TLockedValue(const TLockedValue<TType>& other) {
m_value = other.Get();
}
TLockedValue(const TType& value) {
m_value = value;
}
TLockedValue() {
}
// Gets the resource
operator TType() {
return Get();
}
// Sets the resource
const TType operator = (const TType& newValue) {
Set(newValue);
return newValue;
}
const TType operator = (const TLockedValue& other) {
return *this = other.Get();
}
// Compares the resource
bool operator == (const TType& otherValue) {
return Get() == otherValue;
}
bool operator == (const TLockedValue& other) {
if (this == &other)
return true;
else
return Get() == other.Get();
}
bool operator != (const TType& otherValue) {
return Get() != otherValue;
}
bool operator != (const TLockedValue& other) {
if (this == &other)
return false;
else
return Get() != other.Get();
}
protected:
// Gets the resource
const TType Get() {
// Lock the lock
TLocker locker;
locker.Lock(*this);
// And return the resource
volatile TType value = m_value;
return value;
}
// Sets the resource
void Set(const TType& newValue) {
// Lock the lock
TLocker locker;
locker.Lock(*this);
// And set the resource
m_value = newValue;
}
private:
// The resource to control
volatile TType m_value;
};
#endif // INCLUDE_TWOLOGS_COMMON_LOCK_H
/*******************************************************************************
Version: 8
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(TLock* lockPtr, bool isFirstLock) {
}
// Raised when the given lock is unlocked
void ILockCallback::OnUnlocked(TLock* lockPtr, bool isLastLock) {
}
// Constructor
TLock::TLock(ILockCallback* callbackPtr):
m_callbackPtr(callbackPtr),
m_lockCount(0) {
InitializeCriticalSection(&m_lock);
}
// Destructor
TLock::~TLock() {
DeleteCriticalSection(&m_lock);
}
// Tries to lock the lock
void TLock::Lock() {
EnterCriticalSection(&m_lock);
++m_lockCount;
if (m_callbackPtr != nullptr) {
m_callbackPtr->OnLocked(this, m_lockCount == 1);
}
}
// Tries to unlock the lock
void TLock::Unlock() {
--m_lockCount;
if (m_callbackPtr != nullptr) {
m_callbackPtr->OnUnlocked(this, m_lockCount == 0);
}
LeaveCriticalSection(&m_lock);
}
// The number of times the lock is locked
long TLock::LockCount() {
return InterlockedCompareExchange(&m_lockCount, 0, 0);
}
// Constructor
TLocker::TLocker() {
}
TLocker::TLocker(TLock& lock) {
Lock(lock);
}
TLocker::TLocker(const TLocker& otherLocker) {
m_lockPtrs = otherLocker.m_lockPtrs;
for (TLock* nextLockPtr: m_lockPtrs) {
nextLockPtr->Lock();
}
}
// Destructor
TLocker::~TLocker() {
Unlock();
}
// Also locks the given lock
void TLocker::Lock(TLock& lock) {
lock.Lock();
m_lockPtrs.insert(&lock);
}
// Prematurely unlocks the lock(s)
void TLocker::Unlock() {
for (TLock* nextLockPtr: m_lockPtrs) {
nextLockPtr->Unlock();
}
m_lockPtrs.clear();
}
// Copying
TLocker& TLocker::operator = (const TLocker& otherLocker) {
if (this != &otherLocker) {
Unlock();
m_lockPtrs = otherLocker.m_lockPtrs;
for (TLock* nextLockPtr: m_lockPtrs) {
nextLockPtr->Lock();
}
}
return *this;
}