Introduction

When developing multi-threaded/multi-process application, the synchronization is one of the big issues. In a case where you have to build your same application for single-threaded/multi-threaded/multi-process environments, then managing the synchronization object is another issue. Having lock (synchronization) framework won't just automatically solve all of the above issues, however it will help finding the way to solve those issues. So this article explains how you can build your own simple lock (synchronization) framework, and how you can use them within your development projects.

Many professional developers might already have their own advanced lock (synchronization) framework, or they might use well-known popular library such as boost library, or Intel TBB. However, sometimes these advanced libraries might be over-kill for your application, and they might not be in your taste. After reading this article, you will be able to build your own lock (synchronization) framework as your taste.

This article will explain the following:

BaseLock Class

Critical Section Class

Semaphore Class

Mutex Class

NoLock Class

Auto-release Lock

Use-case examples

** Warning: This article is not about an use-case explanation of an advanced lock framework, nor explanation of differences of synchronization primitives.
As stated in the topic, this article only explains "how to build a simple lock framework", and it is your decision to use/change/modify/improve the given framework
according to your project and/or development environment.

Background

You have at least understand the concept of below synchronization primitives:

Critical Section

Semaphore

Mutex

and also differences between them.

It is optional, but understanding the MFC Synchronization classes (CSingleLock/CMultiLock,
CCriticalSection,
CSemaphore, and
CMutex) will allow you to implement the lock framework given here with the MFC Synchronization Classes instead of using Windows Default Library functions.

Critical Section

Critical sections are working in the user mode unless there is a need to enter the kernel mode. If a thread tries to run a code that is caught be a critical section, it first does a spin blocking and after a specified amount of time, it enters the kernel mode to wait for the critical section. Actually, a critical section consists of a spin counter and a semaphore; the former is for the user mode waiting, and the later is for the kernel mode waiting (sleeping). In Win32 API, there is a CRITICAL_SECTION structure that represents critical section objects. In MFC, there is a class named CCriticalSection. Conceptually, a critical section is a sector of source code that is needed in integrated execution, that is, during the execution of that part of the code it should be guaranteed that the execution will not be interrupted by another thread. Such sectors of code may be required in cases when there is a need to grant a single thread the monopoly of using a shared resource.

Mutex

Mutexes, like critical sections, are designated to protect shared resources from simultaneous accesses. Mutexes are implemented inside the kernel and thus they enter the kernel mode to operate. A mutex can perform synchronization not only between different threads but also between different processes. Such a mutex should have a unique name to be recognized by another process (such mutexes are called named mutexes).

Semaphore

In order to limit the number of threads that use shared resources we should use semaphores. A semaphore is a kernel object. It stores a counter variable to keep track of the number of threads that are using the shared resource. For example, the following code creates a semaphore by the MFC CSemaphore class which could be used to guarantee that only 5 threads at a maximum would be able to use the shared resource in a given time period (this fact is indicated by the first parameter of the constructor). It is supposed that no threads have captured the resource initially (the second parameter):

HANDLE g_sem=CreateSemaphore(NULL,5,5,NULL);

More Info.

Purpose of the Lock Framework

Well-known synchronization primitives in Windows development, are
Critical Section, Semaphore, and
Mutex. For novice developers, they might spend hard time to understand the concept of synchronization itself, but using different synchronization primitives might be just too much. Therefore the idea of this simple Lock Framework is to let different synchronization primitives to have same interface and use them in same manner, but work as their original purpose during run-time.

BaseLock Class

While different synchronization primitives exist for their own purposes, they still share some basic functionality.

Lock

TryLock

TryLockFor (TryLock for certain period of time)

Unlock

Some people might come up with the idea of more functionality, but it will be choice of yours when building your own framework. I will just focus on only those four functionality
for the simplicity.

So our BaseLock class will be a pure virtual class, and will be simple as below.

Note thatLock function returns boolean for Mutex case, for all other cases, it always returns true.

Critical Section Class

Since the CRITICAL_SECTION struct already exist, I just named our class as CriticalSectionEx.
Our CriticalSectionEx class will be a sub-class of BaseLock class, and also it will be an interface class for CRITICAL_SECTION object.

So our CriticalSectionEx class will be as follow, which will be very similar to our BaseLock class.

Note that, the reason that "operator=" returns itself without doing anything is that I didn't want CRITICAL_SECTION object to be replaced by other object. If "operator=" is not implemented, when copy-operator is called, it will automatically call default copy-operator, and the values of CRITICAL_SECTION object will be replaced with other object's value. And the reason, I didn't make it private is that, if copy-operator is private, when some class A holds an CriticalSectionEx object, and when an object of class A tries to copy from other object, it will result an compiler-error.

Also, note that, since the Critical Section is re-entrant, and since difference in entering and leaving count will lead to undefined behavior, m_lockCounter is added to track the number of locks/unlocks. (Thanks to Orjan Westin for the suggestion!)

There are five important functions to know when using Critical Section object in general.

InitializeCriticalSection

DeleteCriticalSection

EnterCriticalSection

LeaveCriticalSection

TryEnterCriticalSection

For more details of each function's functionality, please refer to
MSDN.

Above code snippet is implementation of constructor and copy-constructor. By calling "InitializeCriticalSection" within constructor,
it is automatically initializing the CRITICAL_SECTION object when the CriticalSectionEx object is created.

Also note that, the reason, that copy-constructor is not copying anything but only initializing the
CRITICAL_SECTION object as constructor, is same reason as "operator="

This is very straight forward implementation. When "Lock" function is called, it is entering the critical section by calling
"EnterCriticalSection" function. And when "TryLock" function is called, it tries to enter the critical section
by calling "TryEnterCriticalSection." (Also increment the m_lockCounter for successful locking)

function originally (to avoid deadlock according to MSDN). So I had to do some trick to simulate the TryLock for certain period of time, however use this with care since there is reason that MS did not implemented this functionality for
CRITICAL_SECTION object.

CRITICAL_SECTION object, this does NOT guarantee the order of entering the critical section.

What this function does is as the following

Try to enter the critical section using TryEnterCriticalSection

If succeeded, return with code from TryEnterCriticalSection

If failed, Wait for the CRITICAL_SECTION object to be released with time given from caller

If released, try to enter the Critical Section again using TryEnterCriticalSection.

Note that, the reason that "operator=" returns itself without doing anything is that I didn't want values of Semaphore object to be replaced by other object. If "operator=" is not implemented, when copy-operator is called, it will automatically call default copy-operator, and the values of Semaphore object (such as
m_sem, m_lpsaAttributes, and m_count in this case) will be replaced with other object's value. And the reason, I didn't make it private is that, if copy-operator is private, when some class A holds an Semaphore object, and when an object of class A tries to copy from other object, it will result an compiler-error.

Also note that, the constructor now takes in parameters such as count,
semName, lpsaAttributes where BaseLock and CriticalSectionEx have no parameters for the constructor. This still can be used without the arguments since they have default arguments, and each parameter's detail is as the following:

count represent the lock count of the semaphore

the default value is "1," which represents the binary semaphore.

semName represents the name of the semaphore.

the default value is "NULL"

This might be needed if the semaphore will be used across the processes. (Please refer to MSDN.)

lpsaAttributes represent the security descriptor for new Semaphore.

the default value is "NULL," which represents default security descriptor.

Above code snippet is implementation of constructor and copy-constructor. By calling "CreateSemaphore" within constructor, it is automatically creating the Semaphore handle when the
Semaphore object is created.

Also note that, in Semaphore's case, copy-constructor is copying
m_count and m_lpsaAttributes, and creates new
Semaphore object. Basically this will have same number of lock count and security descriptor as given
Semaphore object, but Semaphore object will be a new instance.

Destructor

// Semaphore.cpp
Semaphore::~Semaphore()
{
CloseHandle(m_sem);
}

If the Semaphore handle is created, it must be closed. So by calling "CloseHandle" in destructor, the Semaphore handle will be automatically deleted, when Semaphore object is deleted/destroyed.

Unlock

If the Semaphore is obtained, it must be released. So by calling "ReleaseSemaphore" within
Unlock function, the Semaphore handle can be released when
Unlock function is called.

Release

As Ahmed Charfeddine suggested, for semaphore, "it is important that Unlock would accept a release count parameter and return a success/failure status." Since this is semaphore specific function, you can just extend Semaphore Class by introducing a new function "Release".

Note that, the reason that "operator=" returns itself without doing anything is that I didn't want values of Mutex object to be replaced by other object. If "operator=" is not implemented, when copy-operator is called, it will automatically call default copy-operator, and the values of Mutex object (such as m_mutex, and m_lpsaAttributes in this case) will be replaced with other object's value. And the reason, I didn't make it private is that, if copy-operator is private, when some class A holds an Mutex object, and when an object of class A tries to copy from other object, it will result an compiler-error.

Also note that, the constructor now takes in parameters such as mutexName, lpsaAttributes which is similar to Semaphore's constructor. This still can be used without the arguments since they have default arguments, and each parameter's detail is as the following:

mutexName represents the name of the semaphore.

the default value is "NULL"

This might be needed if the mutex will be used across the applications. (Please refer to MSDN.)

lpsaAttributes represent the security descriptor for new Semaphore.

the default value is "NULL," which represents default security descriptor.

Note that, the Mutex can be abandoned. However, I didn't want to change the interface just for Mutex, so I added a function, IsMutexAbandoned to check whether the Mutex is abandoned or not when the locking failed. (Thanks to Orjan Westin for the suggestion.)

There are four important functions to know when using Mutex object in general.

CreateMutex

CloseHandle

WaitForSingleObject

ReleaseMutex

For more details of each function's functionality, please refer to MSDN.

Above code snippet is implementation of constructor and copy-constructor. By calling "CreateMutex" within constructor, it is automatically creating the Mutex handle when the Mutex object is created.

Also note that, in Mutex's case, copy-constructor is copying m_lpsaAttributes, and creates new Mutex object. Basically this will have same security descriptor as given Mutex object, but Mutex object will be a new instance.

Destructor

// Mutex.cpp
Mutex::~Mutex()
{
CloseHandle(m_mutex);
}

If the Mutex handle is created, it must be closed. So by calling "CloseHandle" in destructor, the Mutex handle will be automatically deleted, when Mutex object is deleted/destroyed.

Note that for all Locking function, it checks for whether Mutex is abandoned or not, and set the member flag variable, m_isMutexAbandoned, for status. So when the locking failed, you can call "IsMutexAbandoned" function to check whether fail is due to abandoned Mutex.

Unlock

// Mutex.cppvoid Mutex::Unlock()
{
ReleaseMutex(m_mutex);
}

If the Mutex is obtained, it must be released. So by calling "ReleaseMutex" within Unlock function, the Mutex handle can be released when Unlock function is called.

NoLock Class

NoLock class is like a placeholder for Lock Framework in single-threaded environment. When building same application for multiple environment such as single threaded, multi-threaded, multi-process, NoLock will take the role as placeholder for single-threaded environment. Our Nolock class will be a sub-class of BaseLock class.

So our NoLock class will be as follow, which will be very similar to our BaseLock class.

Basically, this NoLock class does not do anything, but just returns true when it is needed.

Auto-release Lock

When developing synchronization, sometimes it is very annoying to match "Lock" and "Unlock." So creating a auto-release mechanism for a structure body, can be very helpful in such a case. By extending "BaseLock" class, this can be easily implemented as below:

As above example, create LockObj object as a local variable (in this case, lock) within if statement with the someLock pointer, which points to CriticalSectionEx object, then the critical section is automatically entered.

Since we implemented "Unlock" within the destroctor of LockObj, and as C++ mechanism, when leaving the if statement, the local variable "lock" will be automatically deleted, and will leave critical section automatically.

More Practical Example Sources

Conclusion

As I said in Introduction, there are many advanced Lock Framework library such as boost library, or Intel TBB. However, in many cases, they might be little over-kill to link with your project. This is a very simple guide to show how you can build a Lock Framework of your own. You can change or expand the functionality as you like according to your needs or taste. Hope this helps to your painful synchronization development.

I think your false copying scheme is flawed, in particular for semaphore and mutex, which do support copying through names. If I have a named Mutex and copy it to another place, I would expect the new Mutex to refer to the same underlying mutex, not a different one. In general, you should avoid promising something you can't keep - if you can't make a true copy, don't offer it.

Furthermore, you're using an internal, undocumented LockSemaphore member of CRITICAL_SECTION - there's no guarantee whatsoever this will be available on all versions of Windows.

Also, a CRITICAL_SECTION is re-entrant - it can be entered multiple times by the same thread, and, if deleted before it has been left as many times, will lead to undefined behaviour. If you're encapsulating it, you also ought to count succesful locks/unlocks.

You should also check if a mutex has been abandonded, not just if the wait has succeeded.

I think I explained well enough for the reason, why I used false copying scheme. It is rather philosophical choice, that I made. So what do you exactly mean by don't offer it? Are you saying don't bother to implement it, so it will use default copy-constructor or copy-operator, which will lead to more logic errors? Or do you mean that make them as private, so every class that has synchronization object, has to make their copy-construct or copy-operator as private? I tend to implement all copy-constructor and copy-operator even default copy-constructor and copy-operator are good enough. This actually helps to relieve more copying error. The philosophy might be different among developers, and if you don't like something, that doesn't mean it is wrong or flawed. I am not saying you are wrong (You do have point,too. It is just different from mine.), and If you want to have true copy, you are more than welcome to change the source code and use it for your own good.

Also, yes, using "LockSemaphore" is actually dangerous as you said, I wanted to unify the interface for every synchronization objects, and that was only choice, I had. If you feel un-safe about it, you can remove them from your Lock Framework.

I agree with your last two suggestions. Yes, adding manual counter for CRITICAL_SECTION, and checking for abandoned mutex will help a lot in debugging.

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David CrowNever mind - my own stupidity is the source of every "problem" - Mixture

Thank you for your comment.
Just what do you mean by original? Are you referring to the comments in the source code?
If you are referring to the comments in the source code, there is no difference in functionality, since the source codes are from my own library.
(the library just has some extra debugging code and Interlocked lock primitive in addition to other lock primitive)

See there is nothing new, as these classes are already provided by MFC and

The person who downvoted me, please come forward and raise his voice

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David CrowNever mind - my own stupidity is the source of every "problem" - Mixture

though it deserve 5 vote, however it would be nice if you provide benefits and comparison sheet for all the synchronization objects in this article

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David CrowNever mind - my own stupidity is the source of every "problem" - Mixture

however it would be nice if you provide benefits and comparison sheet for all the synchronization objects in this article

Yes I agree that as AmrThabet also mentioned the similar suggestion, and I will update the article soon.

And related to your first comment, actually the first version of this article's source code was a wrapper framework of MFC Synchronization classes. (using CSingleLock, CSemaphore, CMutex, etc.)
However I changed to Windows Library since in many cases linking MFC Library can be problematic for some situations such as when a project tries to link with many other external libraries. (conflict may occur during linking process while compiling).
So that is the main reason that I created the source based on Window's default Synchronization.

As functionality wise, if you ask if this framework is any better than MFC Synchronization classes, I would say yes and no.
MFC Synchronizaion classes might be safer, performance wise better, and functionality wise richer, or maybe not. That was not the point of this article. MFC does not supply the source code (it's supplied with library and headers), nor the MFC Synchronization classes are developed in templates (which source code must be supplied). (Of course, many developers would likely know how MFC Synchronization classes implemented, but that does not mean every one of developers does.) This article is NOT about "I have created a cool synchronization framework which is better than rest of other synchronization frameworks, so use this framework instead of other frameworks." It clearly said on the TITLE (HOW TO CREATE A SIMPLE LOCK FRAMEWORK), and left to the developers about changes or improvement according to their projects or development environments.

And again thank you for your comment, and wish this answers your question.

(p.s. I am not the one who downvoted your comment, I welcome all comments. )

I am not sure what the basis of your comment is but as long as I have used MFC, which has been for more than twenty years, source code has come with the library. It did back then and it still does with VS2015.

Imagine you copy an existing object A to object B - now your object B has no sync object, so you cannot synchronize your access to it.

You should either implement the assignment operator for an object to create a new instance of underlying synchronization primitive (mutex, semaphore, critical, whatever) or should make the assignment operator private or should raise an error (at least an assert) for each attempt to lock on copied objects.

Hmm but dummy assignment operators doesn't mean object B has no sync object.
If object B already had a sync object which is constructed by constructor, then when you copy object A to object B, it simply doesn't copy the sync object (which doesn't mean it deletes object which existed already). And same goes for assignment operator, it is just not copying the object from the other but it does have a sync object instance.

I didn't make it private, because if for example, object A and B holds a sync object and you want to copy object A to object B, if the copy operator is private, this will result compiler error. so dummy assignment operator is to support those cases. But object A and B do hold the sync objects (they are just different instances).

If this is not what you meant, can you explain the problem in more detail?