Skip navigation links
IT services and product development
Menu
TwoLogs
IT services and product development

Source code for C_Lock

Download

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.

Description

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.

Information

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.

Files

Each file belonging to this source code module is listed below.

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.

*******************************************************************************/

#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

Lock.cpp

/*******************************************************************************

  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;
}