Click here to Skip to main content
15,879,474 members
Articles / Programming Languages / C++
Article

A "synchronized" statement for C++ like in Java

Rate me:
Please Sign up or sign in to vote.
4.10/5 (25 votes)
18 Dec 20052 min read 139K   27   14
Design and implementation of a "synchronized" C++ statement that works like the one in Java.

Introduction

This article shows how to code a synchronized statement in C++ that works in a similar way to Java. The target of this code is to make a piece of code like the following, compilable and executable in C++:

synchronized(myMutex)
{
    //TODO put synchronized code here
}

The Mutex class

The following piece of code presents a mutex class with lock/unlock semantics (common in many libraries):

//mutex class
class Mutex
{
public:
    //the default constructor
    Mutex()
    {
        InitializeCriticalSection(&m_criticalSection);
    }

    //destructor
    ~Mutex()
    {
        DeleteCriticalSection(&m_criticalSection);
    }

    //lock
    void lock()
    {
        EnterCriticalSection(&m_criticalSection);
    }

    //unlock
    void unlock()
    {
        LeaveCriticalSection(&m_criticalSection);
    }

private:
    CRITICAL_SECTION m_criticalSection;
};

There is nothing particularly special for the above class:

  • it initializes the critical section upon construction,
  • it deletes the critical section,
  • the method lock() locks the critical section, and
  • the method unlock() unlocks the critical section.

We are going to use critical sections, but any synchronization primitive applies.

The Lock class

In order to be consistent with the C++ established code practices, we need a special class for implementing the RAII (Resource Acquisition Is Initialisation) pattern. The following piece of code shows such a class:

//synchronization controller object
class Lock
{
public:
    //the default constructor
    Lock(Mutex &mutex) : m_mutex(mutex), m_locked(true)
    {
        mutex.lock();
    }

    //the destructor
    ~Lock()
    {
        m_mutex.unlock();
    }

    //report the state of locking when used as a boolean
    operator bool () const
    {
        return m_locked;
    }

    //unlock
    void setUnlock()
    {
        m_locked = false;        
    }

private:
    Mutex &m_mutex;
    bool m_locked;
};

Things to note for this class:

  • it locks the mutex upon construction, and
  • it unlocks the mutex upon destruction.

Using the above class is pretty straightforward:

Mutex mutex1;
...
Lock lock1(mutex1);
//synchronized code here

The "synchronized" macro

The synchronized statement can be coded as a macro like this:

#define synchronized(M)  for(Lock M##_lock = M; M##_lock; M##_lock.setUnlock())

where, the parameter M is the mutex variable to use for locking.

Example of using the "synchronized" macro

The following piece of code shows how to use the synchronized macro: it coordinates two threads that print the alphabet in the standard output. Without synchronization, the output is not correct:

//thread count
int thread_count = 0;

//mutex
Mutex mutex1;

//example thread
DWORD CALLBACK thread_proc(LPVOID params)
{
    for(int i = 0; i < 10; ++i)
    {
        synchronized(mutex1)
        {
            for(char c = 'A'; c <= 'Z'; ++c)
            {
                cout << c;
            }
            cout << endl;
        }
    }
    thread_count--;
    return 0;
}

//main
int main()
{
    thread_count = 2;
    CreateThread(0, 0, thread_proc, 0, 0, 0);
    CreateThread(0, 0, thread_proc, 0, 0, 0);
    while (thread_count) Sleep(0);
    getchar();
    return 0;
}

How it works

The macro exploits the nature of the for statement of C++ to do the following (in the presented order):

  1. initialization part: a local lock variable is defined that locks the given mutex; the lock variable contains an internal flag which is set to true.
  2. test part: the lock variable is tested and found to be true: the code inside the loop is executed.
  3. increment part: the lock variable's internal flag is set to false.
  4. test part: the lock variable is tested and found to be false: the loop exits.
  5. exit part: the lock variable is destroyed, unlocking the mutex.

Advantages over classic RAII

Using this way to code RAII has some advantages over the traditional method:

  • it makes the code more readable,
  • it helps avoiding declaration of lock variables, and
  • it ties the code to be synchronized with the synchronization scope.

Notes

The synchronized macro is exception-safe, since it unlocks its mutex upon destruction.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Greece Greece
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralNeeds Improvement Pin
David Patrick19-Dec-05 0:02
David Patrick19-Dec-05 0:02 
GeneralRe: Needs Improvement Pin
Achilleas Margaritis19-Dec-05 1:22
Achilleas Margaritis19-Dec-05 1:22 
GeneralRe: Needs Improvement Pin
Roland Pibinger19-Dec-05 11:50
Roland Pibinger19-Dec-05 11:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.