Our website uses cookies to enhance your browsing experience.
Accept
to the top
>
>
>
V1089. Waiting on condition variable wi…
menu mobile close menu
Analyzer diagnostics
General Analysis (C++)
General Analysis (C#)
General Analysis (Java)
Micro-Optimizations (C++)
Diagnosis of 64-bit errors (Viva64, C++)
Customer specific requests (C++)
MISRA errors
AUTOSAR errors
OWASP errors (C++)
OWASP errors (C#)
Problems related to code analyzer
Additional information
toggle menu Contents

V1089. Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wake-up.

Aug 09 2022

This diagnostic rule is based on the CP.42 CppCoreGuidelines.

The analyzer has detected one of the non-static member functions of the 'std::condition_variable' class template — 'wait', 'wait_for' or 'wait_until' — is called without a predicate. This can lead to a spurious wakeup of a thread or a thread hanging.

Let's consider the example N1 leading to a potential hanging:

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

std::mutex mtx;
std::condition_variable cond;

void consumer()
{
  std::unique_lock<std::mutex> lck { mtx };
  std::cout << "Waiting... " << std::endl;
  cond.wait(lck);                           // <=
  std::cout << "Working..." << std::endl;
}

void producer()
{
  {
    std::lock_guard<std::mutex> _ { mtx };
    std::cout << "Preparing..." << std::endl;
  }

  cond.notify_one();
}

int main() 
{
  std::thread c { consumer };
  std::thread p { producer };

  c.join();
  p.join();
}

The example contains a race condition. The program can hang if it runs in the following order:

  • the 'p' thread wins the race, acquires the mutex first, prints the message to the 'std::cout' and releases the mutex;
  • the 'c' thread acquires the mutex, but does not have time to block on the conditional variable 'cond';
  • the 'p' thread notifies about the event by calling the 'cond.notify_one()' call;
  • the 'c' thread waiting for notification blocks on the conditional variable 'cond'.

To fix this, we should modify the code as follows:

  • The notifying thread should acquire a mutex and change some shared state while the lock is held. For example, a Boolean variable.
  • The waiting thread should call the overload of 'std::condition_variable::wait' that accepts the predicate. Inside the predicate we need to check whether the shared state changed or not.

Here is the fixed example:

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

std::mutex mtx;
std::condition_variable cond;

bool pendingForWorking = false; // <=

void consumer()
{
  std::unique_lock<std::mutex> lck { mtx };
  std::cout << "Waiting... " << std::endl;
  
  cond.wait(lck, [] { return pendingForWorking; }); // <=
  std::cout << "Working..." << std::endl;
}

void producer()
{
  {
    std::lock_guard<std::mutex> _ { mtx };
    pendingForWorking = true;                 // <=
    std::cout << "Preparing..." << std::endl;
  }

  cond.notify_one();
}

int main() 
{
  std::thread c { consumer };
  std::thread p { producer };

  c.join();
  p.join();
}

Let's consider the example N2 where a spurious wakeup can happen:

#include <iostream>
#include <fstream>
#include <sstream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::queue<int> queue;
std::mutex mtx;
std::condition_variable cond;

void do_smth(int);

void consumer()
{
  while (true)
  {
    int var;

    {
      using namespace std::literals;
      std::unique_lock<std::mutex> lck { mtx };
      if (cond.wait_for(lck, 10s) == std::cv_status::timeout) // <=
      {
        break;
      }

      var = queue.front();
      queue.pop();
    }

    do_smth(var);
  }
}

void producer(std::istream &in)
{
  int var;
  while (in >> var)
  {
    {
      std::lock_guard<std::mutex> _ { mtx };
      queue.push(var);
    }

    cond.notify_one();
  }
}

void foo(std::ifstream &fin, std::istringstream &sin)
{
  std::thread p1 { &producer, std::ref(fin) };
  std::thread p2 { &producer, std::ref(sin) };
  std::thread p3 { &producer, std::ref(std::cin) };

  std::thread c1 { &consumer };
  std::thread c2 { &consumer };
  std::thread c3 { &consumer };

  p1.join(); p2.join(); p3.join();
  c1.join(); c2.join(); c3.join();
}

A spurious wakeup happens when a waiting thread wakes up and discovers that the condition it was expecting has not been met. This can occur in two scenarios:

  • The notifying thread changes the shared state and sends a notification. One thread wakes up, processes the shared state, and falls asleep. After that another thread also wakes up from the notification but finds that the shared state has already been processed.
  • The waiting thread is woken up even if the notifying thread has not yet sent a notification. This can happen in some implementations of multithreaded APIs, for example, WinAPI, POSIX Threads, etc.

In the example N2, a spurious wakeup can occur in threads 'c1', 'c2', and 'c3'. As a result of such a wakeup, the queue may be empty — accessing it, we can get an undefined behavior.

To fix this, we should also call the 'std::condition_variable::wait_for' overload that accepts the predicate. Inside the predicate, we need to check whether the queue is empty or not:

void consumer()
{
  while (true)
  {
    int var;

    {
      using namespace std::literals;
      std::unique_lock<std::mutex> lck { mtx };
      bool res = cond.wait_for(lck,
                               10s,
                               [] { return !queue.empty(); }); // <=
      if (!res)
      {
        break;
      }

      // no spurious wakeup
      var = queue.front();
      queue.pop();
    }

    do_smth(var);
  }
}

This diagnostic is classified as:

You can look at examples of errors detected by the V1089 diagnostic.

close form

Fill out the form in 2 simple steps below:

Your contact information:

Step 1
Congratulations! This is your promo code!

Desired license type:

Step 2
Team license
Enterprise license
** By clicking this button you agree to our Privacy Policy statement
close form
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
* By clicking this button you agree to our Privacy Policy statement

close form
Free PVS‑Studio license for Microsoft MVP specialists
* By clicking this button you agree to our Privacy Policy statement

close form
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

close form
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

close form
check circle
Message submitted.

Your message has been sent. We will email you at


If you do not see the email in your inbox, please check if it is filtered to one of the following folders:

  • Promotion
  • Updates
  • Spam