C++11 concurrency: condition variables

In the previous post in this series we have seen the C++11 support for locks and in this post we continue on this topic with condition variables. A condition variable is a synchronization primitive that enables blocking of one or more threads until either a notification is received from another thread or a timeout or a spurious wake-up occurs.

There are two implementations of a condition variable that are provided by C++11:

  • condition_variable: requires any thread that wants to wait on it to acquire a std::unique_lock first.
  • condition_variable_any: is a more general implementation that works with any type that satisfies the condition of a basic lock (basically has a lock() and unlock() method). This might be more expensive to use (in terms of performance and operating system resources), therefore it should be preferred only if the additional flexibility it provides is necessary.

So how does a condition variable work?

  • There must be at least one thread that is waiting for a condition to become true. The waiting thread must first acquire a unique_lock. This lock is passed to the wait() method, that releases the mutex and suspends the thread until the condition variable is signaled. When that happens the thread is awaken and the lock is re-acquired.
  • There must be at least one thread that is signaling that a condition becomes true. The signaling can be done with notify_one() which unblocks one thread (any) that is waiting for the condition to be signaled or with notify_all which unblocks all the threads that are waiting for the condition.
  • Because of some complications in making the condition wake-up completely predictable on multiprocessor systems, spurious wake-ups can occur. That means a thread is awaken even if nobody signaled the condition variable. Therefore it is necessary to check if the condition is still true after the thread has awaken. And since spurious wake-ups can occur multiple times, that check must be done in a loop.

The code below shows an example of using a condition variable to synchronize threads: several “worker” threads may produce an error during their work and they put the error code in a queue. A “logger” thread processes these error codes, by getting them from the queue and printing them. The workers signal the logger when an error occurred. The logger is waiting on the condition variable to be signaled. To avoid spurious wakeups the wait happens in a loop where a boolean condition is checked.

#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <queue>
#include <random>

std::mutex              g_lockprint;
std::mutex              g_lockqueue;
std::condition_variable g_queuecheck;
std::queue<int>         g_codes;
bool                    g_done;
bool                    g_notified;

void workerfunc(int id, std::mt19937& generator)
{
    // print a starting message
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[worker " << id << "]\trunning..." << std::endl;
    }

    // simulate work
    std::this_thread::sleep_for(std::chrono::seconds(1 + generator() % 5));

    // simulate error
    int errorcode = id*100+1;
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[worker " << id << "]\tan error occurred: " << errorcode << std::endl;
    }

    // notify error to be logged
    {
        std::unique_lock<std::mutex> locker(g_lockqueue);
        g_codes.push(errorcode);
        g_notified = true;
        g_queuecheck.notify_one();
    }
}

void loggerfunc()
{
    // print a starting message
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[logger]\trunning..." << std::endl;
    }

    // loop until end is signaled
    while(!g_done)
    {
        std::unique_lock<std::mutex> locker(g_lockqueue);

        while(!g_notified) // used to avoid spurious wakeups 
        {
            g_queuecheck.wait(locker);
        }

        // if there are error codes in the queue process them
        while(!g_codes.empty())
        {
            std::unique_lock<std::mutex> locker(g_lockprint);
            std::cout << "[logger]\tprocessing error:  " << g_codes.front() << std::endl;
            g_codes.pop();
        }

        g_notified = false;
    }
}

int main()
{
    // initialize a random generator
    std::mt19937 generator((unsigned int)std::chrono::system_clock::now().time_since_epoch().count());

    // start the logger
    std::thread loggerthread(loggerfunc);

    // start the working threads
    std::vector<std::thread> threads;
    for(int i = 0; i < 5; ++i)
    {
        threads.push_back(std::thread(workerfunc, i+1, std::ref(generator)));
    }

    // work for the workers to finish
    for(auto& t : threads)
        t.join();

    // notify the logger to finish and wait for it
    g_done = true;
    loggerthread.join();

    return 0;
}

Running this code produce an output that looks like this (notice this output is different with each run because each worker thread works, i.e. sleeps, for a random interval):

[logger]        running...
[worker 1]      running...
[worker 2]      running...
[worker 3]      running...
[worker 4]      running...
[worker 5]      running...
[worker 1]      an error occurred: 101
[worker 3]      an error occurred: 301
[worker 2]      an error occurred: 201
[logger]        processing error:  101
[logger]        processing error:  301
[logger]        processing error:  201
[worker 5]      an error occurred: 501
[logger]        processing error:  501
[worker 4]      an error occurred: 401
[logger]        processing error:  401

The wait() method seen above has two overloads:

  • one that only takes a unique_lock; this one releases the lock, blocks the thread and adds it to the queue of threads that are waiting on this condition variable; the thread wakes up when the the condition variable is signaled or when a spurious wakeup occurs. When any of those happen, the lock is reacquired and the function returns.
  • one that in addition to the unique_lock also takes a predicate that is used to loop until it returns false; this overload may be used to avoid spurious wakeups. It is basically equivalent to:
    while(!predicate()) 
       wait(lock);

As a result the use of the boolean flag g_notified in the example above can be avoided by using the wait overload that takes a predicate that verifies the state of the queue (empty or not):

void workerfunc(int id, std::mt19937& generator)
{
    // print a starting message
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[worker " << id << "]\trunning..." << std::endl;
    }

    // simulate work
    std::this_thread::sleep_for(std::chrono::seconds(1 + generator() % 5));

    // simulate error
    int errorcode = id*100+1;
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[worker " << id << "]\tan error occurred: " << errorcode << std::endl;
    }

    // notify error to be logged
    {
        std::unique_lock<std::mutex> locker(g_lockqueue);
        g_codes.push(errorcode);
        g_queuecheck.notify_one();
    }
}

void loggerfunc()
{
    // print a starting message
    {
        std::unique_lock<std::mutex> locker(g_lockprint);
        std::cout << "[logger]\trunning..." << std::endl;
    }

    // loop until end is signaled
    while(!g_done)
    {
        std::unique_lock<std::mutex> locker(g_lockqueue);

        g_queuecheck.wait(locker, [&](){return !g_codes.empty();});

        // if there are error codes in the queue process them
        while(!g_codes.empty())
        {
            std::unique_lock<std::mutex> locker(g_lockprint);
            std::cout << "[logger]\tprocessing error:  " << g_codes.front() << std::endl;
            g_codes.pop();
        }
    }
}

In addition to this wait() overloaded method there are two more waiting methods, both with similar overloads that take a predicate to avoid spurious wake-ups:

  • wait_for: blocks the thread until the condition variable is signaled or the specified timeout occurred.
  • wait_until: blocks the thread until the condition variable is signaled or the specified moment in time was reached.

The overload without a predicate of these two functions returns a cv_status that indicates whether a timeout occurred or the wake-up happened because the condition variable was signaled or because of a spurious wake-up.

The standard also provides a function called notified_all_at_thread_exit that implements a mechanism to notify other threads that a given thread has finished, including destroying all thread_local objects. This was introduced because waiting on threads with other mechanisms than join() could lead to incorrect and fatal behavior when thread_locals were used, since their destructors could have been called even after the waiting thread resumed and possible also finished (see N3070 and N2880 for more). Typically, a call to this function must happen just before the thread exists.

Below is an example of how notify_all_at_thread_exit can be used together with a condition_variable to synchronize two threads:

std::mutex              g_lockprint;
std::mutex              g_lock;
std::condition_variable g_signal;
bool                    g_done;

void workerfunc(std::mt19937& generator)
{
   {
      std::unique_lock<std::mutex> locker(g_lockprint);
      std::cout << "worker running..." << std::endl;
   }

   std::this_thread::sleep_for(std::chrono::seconds(1 + generator() % 5));

   {
      std::unique_lock<std::mutex> locker(g_lockprint);
      std::cout << "worker finished..." << std::endl;
   }

   std::unique_lock<std::mutex> lock(g_lock);
   g_done = true;
   std::notify_all_at_thread_exit(g_signal, std::move(lock));
}

int main()
{
   // initialize a random generator
   std::mt19937 generator((unsigned int)std::chrono::system_clock::now().time_since_epoch().count());

   std::cout << "main running..." << std::endl;

   std::thread worker(workerfunc, std::ref(generator));
   worker.detach();

   std::cout << "main crunching..." << std::endl;

   std::this_thread::sleep_for(std::chrono::seconds(1 + generator() % 5));

   {
      std::unique_lock<std::mutex> locker(g_lockprint);
      std::cout << "main waiting for worker..." << std::endl;
   }

   std::unique_lock<std::mutex> lock(g_lock);
   while(!g_done) // avoid spurious wake-ups
      g_signal.wait(lock);

   std::cout << "main finished..." << std::endl;

   return 0;
}

That would output either (if the worker finishes work before main)

main running...
worker running...
main crunching...
worker finished...
main waiting for worker...
main finished...

or (if the main finishes work before the worker):

main running...
worker running...
main crunching...
main waiting for worker...
worker finished...
main finished...

Dispelling Common C++ Myths: Static Constructors

Someone may be asked at a C++ interview: What is a static constructor?.
Possible, one interviewer would expect answers like “A static constructor is one called only once” and/or “A static constructor is called before any other instance”, and so on.
Probably, such guy is a little bit confused by another language background (e.g. C# or C++/CLI), or is one learning some concepts and patterns by heart, or is influenced by some misleading articles found over the Internet.

The right answer is neat and clear: “There is no static constructor in standard C++ programming language“.
Not mentioning other arguments, let’s just quote from the C++ standard:

ISO/IEC 14882:2011
12.1 Constructors
4. A constructor shall not be virtual (10.3) or static (9.4).

Still don’t believe that? No problem, even just for fun, try compile next code using any C++ compiler:

class CFoo
{
public:
   static CFoo(); // Error: 'CFoo::CFoo()' cannot be declared static
   // ...
};

Concluding, just have a look then may forget these links if you are a C++ programmer…

…but don’t forget that neither C#, nor C++/CLI is (standard) C++!

Color Picker Listview Control

Among other common Windows dialogs we can find Color Dialog Box that allows to pick a color for our purposes. It can simply be created by a call of ChooseColor WinAPI function or, if using MFC, with the help of CColorDialog class.
Also, the MFC versions shipped with Visual Studio 2008 or newer, include CMFCColorDialog class which shows a little bit more fancy color selection dialog.

However, let’s say we have to make our own custom color picker control (let’s say, looking similar tho one used in Photoshop). Beside making a custom or ActiveX control from scratch, a faster approach is to customize the Windows listview (“SysListView32“) control, kept in MFC by CListCtrl class. There are two ways to customize a listview control: making it custom-draw or making it owner-draw.

Custom Draw Color Picker Listview

Color Picker Listview (custom-draw)

Color Picker Listview (custom-draw)

First we have to handle NM_CUSTOMDRAW notification, sent via WM_NOTIFY.
In subitem pre-paint phase, we can indicate which color has to be used to paint whatever list subitem.
Example

void CColorListCtrl::_HandleSubitemPrePaint(LPNMLVCUSTOMDRAW pNMCD, LRESULT *pResult)
{
   // Note: for a listview, nmcd.dwItemSpec keeps the item index 
   const int nItem = (int)pNMCD->nmcd.dwItemSpec;
   const int nSubItem = pNMCD->iSubItem;
   const int nColor = nItem * m_nColCount + nSubItem;
   if(nColor < m_arrColors.GetSize())    
   {       
      pNMCD->clrTextBk = m_arrColors[nColor]; // m_arrColors is an array of COLORREFs
   }
   // ...
}

Finally, hadle NM_CLICK notification, get the color of the clicked item and send a user message to the parent.
Example

void CColorListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
   LVHITTESTINFO lvhti = {0};
   lvhti.flags = LVHT_ONITEM;
   ::GetCursorPos(&lvhti.pt);
   ScreenToClient(&lvhti.pt);
   int nItem = this->SubItemHitTest(&lvhti);
   if(-1 != nItem)
   {
      const UINT nColor = lvhti.iItem * m_nColCount + lvhti.iSubItem;
      if(nColor < (UINT)m_arrColors.GetSize())       
      {          
         // notify parent          
         CWnd* pWndParent = GetParent();
         if(NULL != pWndParent)
         {             
            pWndParent->SendMessage(CColorListCtrl::WM_COLORPICK, 
               (WPARAM)m_arrColors[nColor]);
         }
      }
   }
   *pResult = 0;
}

This method is pretty simple but it has a disadvantage: we cannot modify item/subitem height. So, if this is necessary we gave to make an owner-draw listview control.

Owner Draw Color Picker Listview

Color Picker Listview (owner-draw)

Color Picker Listview (owner-draw)

An owner-draw listview control must have LVS_OWNERDRAWFIXED style set (“Owner Draw Fixed” property must be set to “True” in the resource editor).
Next, we can handle WM_MEASUREITEM to set item size and override CListCtrl::DrawItem in order to perform custom drawing. Just to note: would be necessary to map by hand the WM_MEASUREITEM message; also DrawItem isn’t called for each subitem.
Example

void CColorListCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMIS)
{
   lpMIS->itemHeight = m_nColorItemHeight;
   lpMIS->itemWidth = m_nColorItemWidth * m_nColCount;
}
void CColorListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
   CDC* pDC = CDC::FromHandle(lpDIS->hDC);
   const UINT nColorCount = (UINT)m_arrColors.GetSize();

   for(UINT nColor = 0; nColor < nColorCount; nColor++)    
   {       
      const UINT nItem = nColor / m_nColCount;       
      if(nItem == lpDIS->itemID)
      {
         UINT nSubItem = nColor % m_nColCount;
         CRect rcColor(0, 0, 0, 0);
         rcColor.top = lpDIS->rcItem.top;
         rcColor.bottom = lpDIS->rcItem.bottom;
         rcColor.left = lpDIS->rcItem.left + nSubItem * m_nColorItemWidth;
         rcColor.right = rcColor.left + m_nColorItemWidth;
         pDC->FillSolidRect(rcColor, m_arrColors[nColor]);
      }
   }
}

Notes

  • Implementation details can be found in the attached demo projects
  • The color pickers presented here are just draft versions intended to demonstrate how to do. You can further improve them by adding new features like loading a custom color palette, adding custom colors on the fly, and so on.
  • A brief presentation of custom-draw and owner draw controls, can be found in this article: Custom Draw vs. Owner Draw Controls.

Downloads

  • Demo project: [download id=”1795″]
  • Demo project: [download id=”1797″]

Custom Draw vs. Owner Draw Controls

While most of Windows developers heard about and eventually used owner-draw controls, fewer know about custom-draw controls. Both types of controls allow customization but they are pretty different. So let’s have a quick look at each one.

Owner Draw Controls

An owner-draw control is a common Windows control that have a special owner-draw style set.

  • BS_OWNERDRAW for button controls;
  • LBS_OWNERDRAWFIXED or LBS_OWNERDRAWVARIABLE for listbox controls;
  • LVS_OWNERDRAWFIXED for listview controls;
  • and so on…

Once the owner-draw style is set, the “owner” has the whole drawing responsibility. For that purpose, have to handle WM_DRAWITEM notification (sent to parent window) and perform drawing. In MFC, can also handle WM_DRAWITEM reflected message in the control’s MFC class or override DrawItem virtual function, if available.
Additionally, in some cases can handle WM_MEASUREITEM, to modify the size of control items. See also WM_COMPAREITEM and WM_DELETEITEM which also can be useful when developing  owner-draw controls.
You can see how owner-draw controls are made by having a look, for example, in the implementation of CBitmapButton and CCheckListBox MFC classes.

Custom Draw Controls

Custom-draw controls have no special style. We say that a control is custom-draw if handles the NM_CUSTOMDRAW notification (sent via WM_NOTIFY message). This case, there is no necessary for the “owner” to perform the whole drawing, but can change some defaults. For example, can tell the system to fill an item with some custom color or perform some additional drawing after the default drawing has been done.
Here is an example of custom-draw listvew control, having alternate item colors and a selection rectangle around a particular subitem.

Custom-Draw Listview Control

Custom-Draw Listview Control

// CustomListCtrl.h

class CCustomListCtrl : public CListCtrl
{
   // ...
   DECLARE_MESSAGE_MAP()
   afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
private:
   void _HandleSubitemPrePaint(LPNMLVCUSTOMDRAW pNMCD);
   void _HandleSubitemPostPaint(LPNMLVCUSTOMDRAW pNMCD);
};
// CustomListCtrl.cpp
   // ...
   ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CCustomListCtrl::OnNMCustomdraw)
END_MESSAGE_MAP()

void CCustomListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
   LPNMLVCUSTOMDRAW pNMCD = (LPNMLVCUSTOMDRAW)pNMHDR;
   *pResult = CDRF_DODEFAULT;

   switch(pNMCD->nmcd.dwDrawStage)
   {
   case CDDS_PREPAINT:
      *pResult |= CDRF_NOTIFYITEMDRAW; // request item draw notify 
      break;
   case CDDS_ITEMPREPAINT:
      *pResult |= CDRF_NOTIFYSUBITEMDRAW; // request sub-item draw notify
      break;
   case CDDS_ITEMPREPAINT|CDDS_SUBITEM: // subitem pre-paint
      _HandleSubitemPrePaint(pNMCD);
      *pResult |= CDRF_NOTIFYPOSTPAINT; // request post-paint notify
      break;
   case CDDS_ITEMPOSTPAINT|CDDS_SUBITEM: // subitem post-paint
      _HandleSubitemPostPaint(pNMCD);
      break;
   }
}

void CCustomListCtrl::_HandleSubitemPrePaint(LPNMLVCUSTOMDRAW pNMCD)
{
   // Note: for a listview, nmcd.dwItemSpec keeps the item index 
   const int nItem = (int)pNMCD->nmcd.dwItemSpec;
   if((nItem % 2) == 0)
   {
      // use custom colors
      pNMCD->clrText = RGB(255,255,255);
      pNMCD->clrTextBk = RGB(128,128,255);
   }
   else
   {
      // use default colors
      pNMCD->clrText = CLR_DEFAULT;
      pNMCD->clrTextBk = CLR_DEFAULT;
   }
}

void CCustomListCtrl::_HandleSubitemPostPaint(LPNMLVCUSTOMDRAW pNMCD)
{
   const int nItem = (int)pNMCD->nmcd.dwItemSpec; 
   const int nSubItem = pNMCD->iSubItem;
   // Note: hard-coded values, just for the demo purpose
   if((22 == nItem) && (3 == nSubItem))
   {
      // Draw rectangle around subitem
      CRect rcSubItem;
      GetSubItemRect(nItem, pNMCD->iSubItem, LVIR_LABEL, rcSubItem);
      CDC* pDC = CDC::FromHandle(pNMCD->nmcd.hdc);
      CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(HOLLOW_BRUSH);
      CPen pen(PS_SOLID, 2, RGB(0,0,128));
      CPen* pOldPen = (CPen*)pDC->SelectObject(&pen);
      pDC->Rectangle(rcSubItem);
      pDC->SelectObject(pOldPen);
      pDC->SelectObject(pOldBrush);
   }
}

Resources

See also

C++11 concurrency: locks

In a previous post I introduced the C++11 support for threads. In this article I will discuss the locking features provided by the standard that one can use to synchronize access to shared resources.

The core syncing primitive is the mutex, which comes in four flavors, in the <mutex> header:

  • mutex: provides the core lock() and unlock() and the non-blocking try_lock() method that returns if the mutex is not available.
  • recursive_mutex: allows multiple acquisitions of the mutex from the same thread.
  • timed_mutex: similar to mutex, but it comes with two more methods try_lock_for() and try_lock_until() that try to acquire the mutex for a period of time or until a moment in time is reached.
  • recursive_timed_mutex: is a combination of timed_mutex and recusive_mutex.

Here is a simple example of using a mutex to sync the access to the std::cout shared object.

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::mutex g_lock;

void func()
{
    g_lock.lock();

    std::cout << "entered thread " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(rand() % 10));
    std::cout << "leaving thread " << std::this_thread::get_id() << std::endl;

    g_lock.unlock();
}

int main()
{
    srand((unsigned int)time(0));

    std::thread t1(func);
    std::thread t2(func);
    std::thread t3(func);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

In the next example we’re creating a simple thread-safe container (that just uses std::vector internally) that has methods like add() and addrange(), with the later implemented by calling the first.

template <typename T>
class container 
{
    std::mutex _lock;
    std::vector<T> _elements;
public:
    void add(T element) 
    {
        _lock.lock();
        _elements.push_back(element);
        _lock.unlock();
    }

    void addrange(int num, ...)
    {
        va_list arguments;

        va_start(arguments, num);

        for (int i = 0; i < num; i++)
        {
            _lock.lock();
            add(va_arg(arguments, T));
            _lock.unlock();
        }

        va_end(arguments); 
    }

    void dump()
    {
        _lock.lock();
        for(auto e : _elements)
            std::cout << e << std::endl;
        _lock.unlock();
    }
};

void func(container<int>& cont)
{
    cont.addrange(3, rand(), rand(), rand());
}

int main()
{
    srand((unsigned int)time(0));

    container<int> cont;

    std::thread t1(func, std::ref(cont));
    std::thread t2(func, std::ref(cont));
    std::thread t3(func, std::ref(cont));

    t1.join();
    t2.join();
    t3.join();

    cont.dump();

    return 0;
}

Running this program results in a deadlock.

The reason for the deadlock is that the tread that own the mutex cannot re-acquire the mutex, and such an attempt results in a deadlock. That’s were recursive_mutex come into picture. It allows a thread to acquire the same mutext multiple times. The maximum number of times is not specified, but if that number is reached, calling lock would throw a std::system_error. Therefore to fix this implementation (apart from changing the implementation of addrange not to call lock and unlock) is to replace the mutex with a recursive_mutex.

template <typename T>
class container 
{
    std::recursive_mutex _lock;
    // ...
};

Then the output looks something like this:

6334
18467
41
6334
18467
41
6334
18467
41

Notice the same numbers are generated in each call to func(). That is because the seed is thread local, and the call to srand() only initializes the seed from the main thread. In the other worker threads it doesn’t get initialized, and therefore you get the same numbers every time.

Explicit locking and unlocking can lead to problems, such as forgetting to unlock or incorrect order of locks acquiring that can generate deadlocks. The standard provides several classes and functions to help with this problems.

The wrapper classes allow consistent use of the mutexes in a RAII-style with auto locking and unlocking within the scope of a block. These wrappers are:

  • lock_guard: when the object is constructed it attempts to acquire ownership of the mutex (by calling lock()) and when the object is destructed it automatically releases the mutex (by calling unlock()). This is a non-copyable class.
  • unique_lock: is a general purpose mutex wrapper that unlike lock_quard also provides support for deferred locking, time locking, recursive locking, transfer of lock ownership and use of condition variables. This is also a non-copyable class, but it is moveable.

With these wrappers we can rewrite the container class like this:

template <typename T>
class container 
{
    std::recursive_mutex _lock;
    std::vector<T> _elements;
public:
    void add(T element) 
    {
        std::lock_guard<std::recursive_mutex> locker(_lock);
        _elements.push_back(element);
    }

    void addrange(int num, ...)
    {
        va_list arguments;

        va_start(arguments, num);

        for (int i = 0; i < num; i++)
        {
            std::lock_guard<std::recursive_mutex> locker(_lock);
            add(va_arg(arguments, T));
        }

        va_end(arguments); 
    }

    void dump()
    {
        std::lock_guard<std::recursive_mutex> locker(_lock);
        for(auto e : _elements)
            std::cout << e << std::endl;
    }
};

Notice that attempting to call try_lock_for() or try_lock_until() on a unique_lock that wraps a non-timed mutex results in a compiling error.

The constructors of these wrapper guards have overloads that take an argument indicating the locking strategy. The available strategies are:

  • defer_lock of type defer_lock_t: do not acquire ownership of the mutex
  • try_to_lock of type try_to_lock_t: try to acquire ownership of the mutex without blocking
  • adopt_lock of type adopt_lock_t: assume the calling thread already has ownership of the mutex

These strategies are declared like this:

struct defer_lock_t { };
struct try_to_lock_t { };
struct adopt_lock_t { };

constexpr std::defer_lock_t defer_lock = std::defer_lock_t();
constexpr std::try_to_lock_t try_to_lock = std::try_to_lock_t();
constexpr std::adopt_lock_t adopt_lock = std::adopt_lock_t();

Apart from these wrappers for mutexes, the standard also provides a couple of methods for locking one or more mutexes.

  • lock: locks the mutexes using a deadlock avoiding algorithm (by using calls to lock(), try_lock() and unlock()).
  • try_lock: tries to call the mutexes by calling try_lock() in the order of which mutexes were specified.

Here is an example of a deadlock case: we have a container of elements and we have a function exchange() that swaps one element from a container into the other container. To be thread-safe, this function synchronizes the access to the two containers, by acquiring a mutex associated with each container.

template <typename T>
class container 
{
public:
    std::mutex _lock;
    std::set<T> _elements;

    void add(T element) 
    {
        _elements.insert(element);
    }

    void remove(T element) 
    {
        _elements.erase(element);
    }
};

void exchange(container<int>& cont1, container<int>& cont2, int value)
{
    cont1._lock.lock();
    std::this_thread::sleep_for(std::chrono::seconds(1)); // <-- simulates the deadlock
    cont2._lock.lock();    

    cont1.remove(value);
    cont2.add(value);

    cont1._lock.unlock();
    cont2._lock.unlock();
}

Suppose this function is called from two different threads, from the first, an element is removed from container 1 and added to container 2, and in the second it is removed from container 2 and added to container 1. This can lead to a deadblock (if the thread context switches from one thread to another just after acquiring the first lock).

int main()
{
    srand((unsigned int)time(NULL));

    container<int> cont1; 
    cont1.add(1);
    cont1.add(2);
    cont1.add(3);

    container<int> cont2; 
    cont2.add(4);
    cont2.add(5);
    cont2.add(6);

    std::thread t1(exchange, std::ref(cont1), std::ref(cont2), 3);
    std::thread t2(exchange, std::ref(cont2), std::ref(cont1), 6);

    t1.join();
    t2.join();

    return 0;
}

To fix the problem, you can use std::lock that guaranties the locks are acquired in a deadlock-free way:

void exchange(container<int>& cont1, container<int>& cont2, int value)
{
    std::lock(cont1._lock, cont2._lock); 

    cont1.remove(value);
    cont2.add(value);

    cont1._lock.unlock();
    cont2._lock.unlock();
}

Hopefully this walktrough will help you understand the basics of the synchronization functionality supported in C++11.

C++11 concurrency: threads

C++11 provides richer support for concurrency than the previous standard. Among the new features is the std::thread class (from the <thread> header) that represents a single thread of execution. Unlike other APIs for creating threads, such as CreateThread, std::thread can work with (regular) functions, lambdas or functors (i.e. classes implementing operator()) and allows you to pass any number of parameters to the thread function.

Let’s see a simple example.

#include <thread>

void func()
{
   // do some work
}

int main()
{
   std::thread t(func);
   t.join();

   return 0;
}

In this example t is a thread object representing the thread under which function func() runs. The call to join blocks the calling thread (in this case the main thread) until the joined thread finishes execution.

If the thread function returns a value, it is ignored. However, the function can take any number of parameters.

void func(int a, double b, const std::string& c)
{
    std::cout << a << ", " << b << ", " << c.c_str() << std::endl;
}

int main()
{
   std::thread t(func, 1, 3.14, "pi");
   t.join();

   return 0;
}

The output is:

1, 3.14, pi

It is important to note that the parameters to the thread function are passed by value. If you need to pass references you need to use std::ref or std::cref.
The following program prints 42.

void func(int& a)
{
   a++;
}

int main()
{
   int a = 42;
   std::thread t(func, a);
   t.join();

   std::cout << a << std::endl;

   return 0;
}

But if we change to t(func, std::ref(a)) it prints 43.

In the next example we execute a lambda on a second thread. The lambda doesn’t do much, except for printing some message and sleeping for a while.

#include <thread>
#include <chrono>
#include <iostream>

auto func = []() {
    std::cout << "thread " << std::this_thread::get_id() << " started" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(rand()%10));
    std::cout << "thread " << std::this_thread::get_id() << " finished" << std::endl;
};

int main()
{
    srand((unsigned int)time(0));

    std::thread t(func);
    t.join();

    return 0;
}

The output looks like this (obviously the id of the threads differ for each run).

thread 5412 started
thread 5412 finished

But if we start two threads, the the output looks different:

    std::thread t1(func);
    std::thread t2(func);
    
    t1.join();
    t2.join();
thread thread 10180 started9908 started

thread 10180 finished
thread 9908 finished

The reason is the function running on a separate thread is using std::cout, which is an object representing a stream. This is a shared object of a class that is not thread-safe, therefore the access from different threads to it must be synchronized. There are different mechanisms provided by C++11 to do that (will discuss them in a later post), but one of them is std::mutex (notice there are four flavors of mutexes).

The correct, synchronized code should look like this:

#include <mutex>

std::mutex m;

auto func = []() {
    m.lock();
    std::cout << "thread " << std::this_thread::get_id() << " started" << std::endl;
    m.unlock();

    std::this_thread::sleep_for(std::chrono::seconds(rand()%10));

    m.lock();
    std::cout << "thread " << std::this_thread::get_id() << " finished" << std::endl;
    m.unlock();
};
thread 5032 started
thread 7672 started
thread 5032 finished
thread 7672 finished

In this examples I have used several functions from the std::this_thread namespace (also defined in the <thread> header). The helper functions from this namespace are:

  • get_id: returns the id of the current thread
  • yield: tells the scheduler to run other threads and can be used when you are in a busy waiting state
  • sleep_for: blocks the execution of the current thread for at least the specified period
  • sleep_util: blocks the execution of the current thread until the specified moment of time has been reached

Apart from join() the thread class provides two more operations:

  • swap: exchanges the underlying handles of two thread objects
  • detach: allows a thread of execution to continue independently of the thread object. Detached threads are no longer joinable (you cannot wait for them).
int main()
{
    std::thread t(funct);
    t.detach();

    return 0;
}

What happens though if a function running on a separate thread throws an exception? You cannot actually catch that exception in the thread that’s waiting for the faulty thread, because std::terminate is called and this aborts the program.

If func() throws an exception, the following catch block won’t be reached.

    try 
    {
        std::thread t1(func);
        std::thread t2(func);
    
        t1.join();
        t2.join();
    }
    catch(const std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }

What can be done then? To propagate exceptions between threads you could catch them in thread function and store them in a place where they can be lately looked-up. Possible solutions are detailed here and here.

In future posts we will look at other concurrency features from C++11 (such as synchronization mechanisms or tasks).

Cinci ani de CODEXPERT

Astazi CODEXPERT implineste cinci ani de existenta. Am initiat acest proiect din dorinta de a aduce impreuna pasionati ai programarii din Romania (si nu numai) si a forma o comunitate a dezvoltatorilor pe tehnologii native. Intentia noastra ramane neschimbata si speram ca in viitor sa reusim mai multe decat pana acum.

Din pacate tehnologiile native (unmanaged) nu beneficiaza de atat de multa publicitate precum cele managed (.NET sau Java). Exista totusi sperante ca odata cu definitivarea noului standard C++ acest lucru sa se schimbe. Trendurile arata ca C++ revine in atentie, inclusiv la marile conferinte organizate de Microsoft, care un deceniu nu a vorbit decat despre .NET.

Pe parcursul acestor cinci ani site-ul nostru a trecut prin diverse schimbari. In centrul sau s-a aflat intotdeauna forumul de discutii, unde pana acum am discutat peste 2000 de subiecte intr-un total de peste 13000 de mesaje. Am publicat articole, proiecte open-source, resurse pentru programatori precum carti gratis sau tutoriale online. Acum, la implinirea a cinci ani de existenta venim cu cateva noutati:

  1. In primul rand lansam acest blog unde intentionam sa abordam diverse subiecte, de la probleme tehnice la diverse lansari de produse. Pentru a ne adresa unei audiente cat mai mari probabil unele posturi vor fi in limba engleza.
  2. In al doilea rand am reproiectat site-ul principal. Noul format este 100% compatibil HTML5+CSS3, insa aceasta inseamna ca navigarea din browsere vechi precum IE7/IE8 va intampina dificulati. Va recomandam sa folositi o versiune de browser mai recenta care sa afiseze corect documente HTML5.

Asteptam parerile si sugestiile voastre atat aici cat si in forum.

Echipa CODEXPERT
(Marius Bancila & Ovidiu Cucu)