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

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.

Files

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

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.

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

#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

Lock.cpp

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

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