0

The below code doesn't block the thread and it gets into an infinite loop and keep printing

Waiting...

Waiting...

My idea is, the thread should block on the max() time and when it's set to now(), it should unblock the thread and do the business. Any ideas? I am using VS2013

#include "stdafx.h"
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <chrono>

std::condition_variable condVar;
std::mutex mut;
bool pred = false;
auto dueAt = std::chrono::steady_clock::time_point::max();

void threadFunc()
{
    for (;;)
    {
        std::unique_lock<std::mutex> lock(mut);
        dueAt = std::chrono::steady_clock::time_point::max();

        if (condVar.wait_until(lock, dueAt, [](){ return pred; }))
        {
            std::cout << "Ready..." << std::endl;
            pred = false;
            dueAt = std::chrono::steady_clock::time_point::max();
        }
        else
        {
            std::cout << "Waiting..." << std::endl;
        }
    }
}

void notifierThread()
{
    for (int i = 0; i < 5; i++)
    {
        std::this_thread::sleep_for(std::chrono::seconds(5));
        {
            std::lock_guard<std::mutex> lock(mut);
            dueAt = std::chrono::steady_clock::now();
            pred = true;
            std::cout << " Signalling..." << std::endl;
        }
        condVar.notify_one();
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    std::thread t1(threadFunc);
    std::thread t2(notifierThread);
    t1.join();
    t2.join();
    return 0;
}
11
  • can't reproduce in vs2022, is there a reason you're using an 11 year old release? Commented Dec 12, 2024 at 8:35
  • It's legacy code base we are supporting. So does that mean it's wait_until() is not doing what it supposed to do on VS2013 then? Commented Dec 12, 2024 at 9:13
  • I don't understand when your wait_until call can ever return false (and print put Waiting...). When it is called, dueAt is set to max, so the timeout cannot be reached. And if the internal waiting in the loop is interrupted, the timeout is either still the same, or, if it has changed, then pred has changed to true as well and the loop ends. Commented Dec 12, 2024 at 9:16
  • 2
    "using a wait_until() that blocks a thread permanently" — This does not make any sense. Commented Dec 12, 2024 at 10:04
  • 1
    Exactly. In my opinion, your code is the same as if you replaced wait_until by wait (plus get rid of that branching). Changing dueAt in notifierThread doesn't affect anything in this program. Commented Dec 12, 2024 at 13:14

1 Answer 1

0

Your compiler is old and has bugs?

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

std::condition_variable condVar;
std::mutex mut;
bool pred = false;
bool shutdown = false;
int result = 0;

void threadFunc()
{
  for (;;)
  {
    std::unique_lock<std::mutex> lock(mut);

    condVar.wait(lock, [](){ return pred || shutdown; });

    if (shutdown) {
      std::cout << "Shutdown!" << std::endl;
      break;
    }
    std::cout << "Ready... #" << result << std::endl;
    pred = false;
    ++result;
  }
}

void notifierThread()
{
  for (int i = 0; i < 5; i++)
  {
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    {
      std::lock_guard<std::mutex> lock(mut);
      pred = true;
      std::cout << " Signalling... #" << i << std::endl;
    }
    condVar.notify_one();
  }
}


int main()
{
    std::thread t1(threadFunc);
    std::thread t2(notifierThread);
    t2.join();
    while (true) {
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      std::lock_guard<std::mutex> l(mut);
      if (result == 5)
      {
        std::cout << " Signalling Shutdown!" << std::endl;
        shutdown = true;
        break;
      }
    }
    condVar.notify_one();
    t1.join();
    return 0;
}

here is a complete terminating version of your program that does not use dueAt.

Manipulating dueAt while you are .wait_untiling should do nothing (barring UB due to a race condition, which you avoided with a lock). It appears your C++ std implementation has a bug.

What, exactly, you where trying to do isn't clear, so I converted your code into something that (probably) halts.

In practice, you can't assume that sending 5 signals this way results in 5 receptions, as the sleep(100 ms) isn't guaranteed to give the reader thread to read. A more proper program would send a counter:

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

std::condition_variable condVar;
std::mutex mut;
int counter = 0;
bool shutdown = false;
int consumed = 0;

void threadFunc()
{
  for (;;)
  {
    std::unique_lock<std::mutex> lock(mut);

    auto old_count = counter;
    condVar.wait(lock, [old_count](){ return (counter != old_count) || shutdown; });

    if (shutdown) {
      std::cout << "Shutdown!" << std::endl;
      break;
    }
    std::cout << "Ready... #" << old_count << ".." << counter << std::endl;
    consumed = counter;
  }
}

void notifierThread()
{
  std::this_thread::sleep_for(std::chrono::milliseconds(250));
  for (int i = 0; i < 5; i++)
  {
    if (i%2)
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    {
      std::lock_guard<std::mutex> lock(mut);
      ++counter;
      std::cout << " Signalling... #" << i << " with count " << counter << std::endl;
    }
    condVar.notify_one();
  }
}


int main()
{
    std::thread t1(threadFunc);
    std::thread t2(notifierThread);
    t2.join();
    while (true) {
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
      std::lock_guard<std::mutex> l(mut);
      if (consumed == 5)
      {
        std::cout << " Signalling Shutdown!" << std::endl;
        shutdown = true;
        break;
      }
    }
    condVar.notify_one();
    t1.join();
    return 0;
}

here I make the producer be erratic, sometimes fast and sometimes slow. The consumer can handle more than 1 signal at a time however.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.